Symfony 3 Forms: Build, Render & Conquer!
Discover the joy of Symfony forms; constructing, handling & conquering them. Learn how to add validation and avoid frequent form-based errors.
About this course
Ah, forms: one of the most powerful - but sometimes hated - features in all of Symfony. Here's the deal: forms are just plain hard. You need to manage the HTML form elements, validation, data transformation and a lot more. The Form component is probably the most complex part of Symfony. But the more you work with it, the more you'll like it.
In this episode, we'll learn how to use Symfony forms to handle simple situations as well as some complex setups. Most importantly, I'll show you how to avoid the pitalls that many developers often fall into that causes the form component to spiral into complexity hell. The form system is a tool: in this tutorial, we'll put the joy back into it:
- Creating a basic form
- Basic form rendering and customization
- Handling a form submit
- Backing your form with an entity
- Adding validation
- Form field guessing
- Pre-filling with default data
- Creating a form "type" class
- Using forms to handle an API POST request
- Understanding how forms really work
- Flash messages
... and form tips and tricks stuck in all over!
Next courses in the Symfony 3: Starting in Symfony 3 section of the Symfony 3 Track!
72 Comments
Yeah, I would love to see it too (dynamic forms especially) ;)
yeah please do this example https://symfony.com/doc/mas...
I've moved this up higher on our list - there's obviously some interest here! :D
Hi I already did this example https://github.com/slk500/D... maybe it will be helpful for someone else
Awesome! Thanks for posting :). I will also have a *small* Form events example on our upcoming collections tutorial: https://knpuniversity.com/s.... But, it's not the most complex example :).
Hey guys,
Here's an example of using FormEvents - check it out in the "Doctrine Collections: ManyToMany, Forms & other Complex Relations" tutorial:
https://knpuniversity.com/s...
ping Bryan Humphreys Bryan Humphreys Bryan Humphreys
Cheers!
Hi Leanna and Ryan. This course is huge (all of your courses are, by the way...)! Thank you so much!
Re ModelClass (multiple entities -> one form): Could you maybe give an example of how to design such a ModelClass? I could imagine these to be - lets say - a pseudo-entity looking like an entity (without database connection) which holds specific properties from different entities. These properties are defined by setters custom querying data from the actual entity.
Am I thinking in the right direction? Could you maybe drop such a ModelClass and also the Controller-lines (GenusAdminController) as of line 67 to save this data-collection? Or just paste in a knowledge-resource which you do recommend.
Thank you so much!
Cheers
Thanks Schmalitz for the really nice comments!
And you are 100% thinking in the right direction - you even explained it really well in my opinion :p. We've gotten a few requests to explain this further, so I think we will in a future tutorial. But, your GenusAdminController would ultimately look something like this (this is totally, fake code - just use it for inspiration!). In this example, I have a "Genus edit form" with just 2 fields: the Genus name and the Genus's SubFamily name. If you changed the SubFamily name, you would actually update that record in the database (it's kind of a weird example):
public function editAction(Genus $genus)
{
// create the model object and "transfer" the data to it from other entities
$genusModel = new GenusModel(); // some non-entity class
$genusModel->setName($genus->getName());
$genusModel->setSubFamilyName($genus->getSubFamily()->getName());
$form = $this->createForm(GenusModelForm::class, $genusModel);
if ($form->isValid()) {
// now, transfer the submitted data back to the entities
$genus->setName($genusModel->getName());
$genus->getSubFamily()->setName($genusModel->getSubFamilyName());
// save the $genus and $genus->getSubFamily() objects
}
// ...
}
I hope that helps!
Super nice - it definitely helps! Thank you so much!
You gals and guys are doing a fantastic job!
Heyho ! While I feel quite comfortable using forms, I'm not sure how to best handle my ajax data. Mostly I have one to three values sent via ajax and finally triggering some service action. Clearly the data should be sanitized and validated on backend side. However, using a specific form model for each case seems a bit overhead to me. Any recommendations ? Thanxbye
Hey @Markus!
I agree :). Forms seem like overkill! So, you have a few options.
1) You could handle those 3 fields manually, but still validate the final entity class to see if there are validation errors. You don't need forms to have validation.
2) You could just handle the validation manually. Really, when I have small AJAX endpoints like this, checking 3 separate values is not a huge deal. And... even more... I don't usually even need to check all 3 - if one of those fields is some user-submitted text... there's not really anything to validate. You only need to validate if there are actually some "wrong" possible values.
Anyways, it *kinda* depends on your setup, but for 1-3 values... I'd go with option (2) - just do the 1-2 checks yourself, and then pass the values to a service once you're happy, like you're already doing :).
Cheers!
Hey Yash,
Yes, for active students. Email us to get more information: https://knpuniversity.com/c...
Cheers!
Hi Bjorn! This will come out later this month - so in May! So, within the next 2 weeks :)
Hi there, great tutorials really !! I was wondering how you would deal with entity inheritance. Let's say you have a User Entity and a Player Entity extending User (based on a doctrine class table inheritance). How would you build and render your form then ? Build a form that is not mapped to an entity with a choice field asking the type of user ? and if we select 'player' we would add fields dynamically ?
Hey @elbarto,
It depends on your business logic. You can use form inheritance too, i.e. PlayerType extends UserType - I think it's the easiest one. Or something like you said, based on an instance of data, i.e. whether it a Player object or a User object - add or hide some extra fields. We have a screencast how to do that here: https://knpuniversity.com/s...
I hope it helps you.
Cheers!
Last track is over quickly :( but thank you very much!
I am looking forward the next one :)
Hey Ryan,
Is there a way to get the submitted data () in the the SUBMIT form event?
I have some unmapped form field as a result of form modification in SUBMIT event. When I submit the form, I can view the unmapped field with $request->request->all() in the controller prior to the $form->handlerequest method.
I try to access the unmapped field in the PRE_SUBMIT and SUBMIT event listeners but I only get the:
- underlying data_class (entity) with the mapped fields of course
- and the original form fields and form fields set in PRE_SET_DATA event listener.
I need the value of the submitted unmapped field to do some form modification on the form.
Could you please point me in the right direction? I 've read something about defining a model class instead of the entity as the 'data_class' in the form, which will have my unmapped fields as properties, any ideas?
Hey Stonehenge Webdev
I usually don't use Form events (I try to do all my work out of the form), but this post may help you https://stackoverflow.com/a...
Cheers!
Hi all.. How to make dependent dropdown in symfony?
I have 4 dependent field (provinces -> regencies -> districts -> villages).
So I want to select a villages, based on the villages in districts and so on..
I already follow the documentation(http://symfony.com/doc/curr..., but still not work for me..
Thank you...
Hey Henri!
Honestly, these are a huge pain with Symfony's form (maybe my *least* favorite thing to do with them). They're hard enough with 2 dependent dropdowns, but 4 makes it even uglier.
If possible, I would recommend refactoring your form to *not* use the Symfony form system, and instead just write some AJAX to handle all of this. If these fields are the only ones that exist on the page, then this is easy. But if they're part of a bigger form, then it's a bit harder, as you probably still want to use the Symfony form system for the rest of the form. In that case, I would still add these to your form, but I would make them hidden fields, and then write JavaScript to create the drop-downs and set the hidden fields' values onChange. If the fields (e.g. provinces, regencies) are drop-downs of entities in your system, then you can do the same thing, but you'll need to add a EntityHiddenType to your app (https://gist.github.com/bjo... and set your fields to this type (this will make them a hidden field, but when you set their value to the entity's id, Symfony will query for the entity using that id on submit).
So... it's complicated. But let me know if this helps!
Cheers!
Hi Ryan, thank you for your answer..
So how to create form without Symfony form system? :)
Hey Henri,
It's pretty simply - just use HTML code in your template :)
<form action="{{ path('some_route_here...') }}" method="POST">
// fields and buttons here...
</form>
Cheers!
Hi Cristian,
We prefer to use ReactJS, but AngularJS is nice too ;)
Cheers!
Seriously, I am about to abandon KNP for codereviewvideos.com if this does not come out this week.
Hi Tom!
Haha, well, we don't want that :). At least I'm glad you're excited about the tutorial. I can temporarily publish the finished code for this tutorial - as you suggested - to keep people productive over the next week or so before we get this out! You can find it here: https://github.com/knpunive.... If you look at the commits (https://github.com/knpunive... you can find the individual steps.
Cheers!
How about this... Even if you do not have the video done, how about releasing the lesson and code.
Hey Ryan, I have gone through the form tutorials on symphony 2 but cant find out how to submit a EntityChoice with multiple set to true (site). If I set multiple to false it works fine, but when I put multiple => true one of the following happens. a) if I choose just one option in the select box I get an error "Expected value of type "Doctrine\Common\Collections\Collection|array" for association field "AppBundle\Entity\User#$sites", got "Doctrine\Common\Collections\ArrayCollection" instead" b) if I tick all available options and press submit it just reloads the page. I have 2 tables users and sites. on the user entity I have
/**
* @ORM\ManyToMany(targetEntity="Site", inversedBy="users")
*/
private $sites;
public function __construct()
{
$this->sites = new arrayCollection();
}
*/ * @return ArrayCollection
/**
public function getSites()
{
return $this->sites;
}
My site entity is as follows:
/**
* @ORM\ManyToMany(targetEntity="User", mappedBy="sites")
*/
private $users;
/**
* @return mixed
*/
public function getUsers()
{
return $this->users;
}
My form element that is causing problems is below
$form = $this->createFormBuilder()
->add('sites', EntityType::class, array(
'class' => 'AppBundle\Entity\Site',
'choice_label' => 'name',
'placeholder' => 'Choose Sites',
'expanded' => true,
'multiple' => true,
))
->add('create', SubmitType::class, array('label' => 'Create User'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$data = $form->getData();
$user->getSites()->add($data['sites']);
$em->persist($user);
$em->flush();
Hey Kosta!
Hmm, it looks fine to me! In fact, it's curious: if you set multiple to false, I would expect this to *fail*, as the EntityType would pass an individual *object* to your User.sites property. Do you have a setSites() method on User? That's the only piece I don't see here. It's also odd that Doctrine is complaining about the ArrayCollection - as it implements the Collection interface class that it says it actually wants :).
Cheers!
I had to enter the $user object into the form builder and after the form->handlerequest() I had to delete the $data object, it was causing the problems. the fix is below, thank anyway ryan, your a legend.
$user = $em->getRepository('AppBundle\Entity\User')
->find($userId);
$form = $this->createFormBuilder($user)
->add('firstName', TextType::class)
->add('lastName', TextType::class)
->add('address', TextType::class)
->add('EmailAddress', TextType::class, array(
'required' => true,
))
->add('password', TextType::class)
->add('phoneNumber', IntegerType::class)
->add('title', TextType::class)
->add('role', EntityType::class, array(
'class' => 'AppBundle\Entity\Role',
'choice_label' => 'role',
'placeholder' => 'Choose Role',
))
->add('sites', EntityType::class, array(
'class' => 'AppBundle\Entity\Site',
'choice_label' => 'name',
'placeholder' => 'Choose Sites',
'required' => false,
'expanded' => true,
'multiple' => true,
))
->add('drivingLicence', TextType::class)
->add('drivingLicenceExpiryDate', DateType::class)
->add('securityLicence', TextType::class)
->add('securityLicenceExpiryDate', DateType::class)
->add('goodQuality', TextType::class)
->add('create', SubmitType::class, array('label' => 'Edit User'))
->getForm();
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$em->persist($user);
$this->addFlash(
'success',
'User details have been updated'
);
$em->flush();
return $this->redirectToRoute('users_list');
}
//I had to delete $data = $form->getData(); and then all the setters after that.
Ha, glad you figured it out one way or another! This setup makes sense to me :)
Hello, I'm starting with forms, and I've a question: I'm using bootstrap_3_horizontal_layout in a form with a collection; In fact, I've an Invoice and a row for each product in this invoice (ProductInvoice). I've followed http://symfony.com/doc/curr... and now I'm able to add a row and delete a row; if I understand correctly, I'm adding an object in real time with a prototype. Everything is working except that I need to show each form_row of ProductInvoice with style class="col-md-3" instead the default class="col-md-10".
I've tried this: http://symfony.com/doc/curr... but the problem is the form_row name since it's dynamic like purchase_invoice_products_1_name.
How can I customize the form_row of a prototype dynamically generated via javascript?
Thank you :)
Maybe this help someone :) I've solved using a macro
{% macro purchasedProduct(product) %}
<div class="row">
<div class="col-md-3">
{{ form_row(product.name)}}
</div>
</div>
{% endmacro %}
and then {% import "form/bootstrap_supplier.html.twig" as supplierForm %}
and in data-prototype="{{ supplierForm.purchasedProduct(form.products.vars.prototype) |e }}"
Now I can use col-md-3 for my fields instead the default col-md-10 with a prototype and a collection :)
Ah, too bad! This is coming out so soon! Well, it'll be ready if/when you come back to Symfony :).
Hi Ryan,
I'd like to see an example of how to save a ViewModel (form with data from different tables). Is it already available somewhere?
Hey Christian!
I don't have an example handy, but let me at least get you started :).
Suppose you have a Product
entity and a Category
entity... and for some reason, you need to create a new form that allows you to create a edit product and its category name at the same time. You form needs to have these fields:
- categoryName
- productName
- productPrice
Here's the process to handle this:
1) Create a new mode class that matches the exact fields you need. So, a ProductCategoryFormModel
class that has categoryName, productName and productPrice fields.
2) Create a form class with the 3 fields and render it. There's nothing special about this form, except that its data_class is set to ProductCategoryFormModel
3) In your controller, you'll do a little extra processing to get this all setup:
/**
* @Route("/products/edit/{id}")
*/
public function editAction(Product $product, Request $request)
{
// create our "model" object by hand from our data
$formModel = new ProductCategoryFormModel();
$formModel->setProductName($product->getName());
$formModel->setProductPrice($product->getPrice());
$formModel->setCategoryName($product->getCategory()->getName());
$form = $this->createForm(ProductCategoryForm::class, $formModel);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// now we need use or model object to update our entities
$product->setName($formModel->getProductName());
$product->setPrice($formModel->getProductPrice());
$product->getCategory()->setName($formModel->getCategoryName());
// persist and flush the entities, then redirect!
}
}
As you can see, it's just a manual process to transform whatever your database data is, into your new model class. Then, after submit, you do the same thing back. That manual works is a little bit annoying, but the benefit is that you have a super simple form that maps exactly to a class. You can also easily add validation constraints to your model class.
Let me know if that helps, and any other questions you have about this :)
Cheers!
Oh I see so you can do $product->getCategory()
because of the dependency injection?
Hey Cristian,
It's not because of the dependency injection, but because of database relationships between Product and Category, e.g. Product relates to the Category as ManyToOne. So you can get a category of product and, if you have setup inverse side, i.e. OneToMany relationship for the Category - you can get all products of a category. See the https://knpuniversity.com/s... for the real example.
Cheers!
Hi there,
Would be nice to see something about of how to manage file uploads in entity forms, like images for users avatar
cheers ;]
hello there again!!! i have an question about forms... can i validate an no entity related form?
I created an form to find something in database, but if i send it clear, will return all, i don't want it....
Hi Raphael!
Yes - you'll attach your annotations via options on the fields: http://symfony.com/doc/curr...
Cheers!
Hey !!! Good news!!! i subscribed the KNPU... Amazing vĂdeos.!!!!
Now i will ask for one more help.... thank you for answer me.... i was with trouble to insert datatype field in forms... i asked for help in Stackoverflow and no one answered yet... if you can, can you explain more to us? i spent 1 day in this problem and can't solve it.... here is my explanation and details about...
http://stackoverflow.com/qu...
Best regards!
You rock! And welcome! I see you got an answer on that SO - did it solve your problem?
Yeah!!!! Finally i finished Azure configuration with Symfony....
If you can, create an video about Upload an Symfony Website to Azure.... It take me 2 days just because little things... now i can configure in 30 minutes... lol... others peoples will like an explanation....
Hey Ryan,
Would be great if advance tutorials like FormEvents are also covered in this Chapter or the next.