It’s been quite a road to understand the symfony2 CollectionType, and to make it work the way I wanted!
This article will be brief (I’ll try at least) and will not explain a lot because this reflexion started 3 articles ago so all the information on why things are made like this, why is my code like that etc… can be understood through those articles:
- Form composition in symfony2
- First shot at the collection field
- Something is broken?
Part of the solutions are in the comments too.
Why make a collection?
First things first: When do you want to use the collection field? Any time you need to add an unknown number of fields to a form. This means anytime you need to add an unknown number of related entities to your “core” entity in my case. Good examples are:
- Holes on a golf course (9? or 18?)
- Tags on a blog article or on a product
- Actors on a movie
- Toppings on a pizza
The use for collection field is when you want to set all those things and the “core” entity at the same time. You don’t want your user to first chose a pepperoni pizza, then validate, go to the next screen add toppings. You want him to create a pepperoni pizza with toppings right away.
Here I made a small ProductBundle where I save products that have tags (like ‘shoes’, ‘blue’, ‘winter’, ‘fashionable’, ‘ugly’, …) The complete bundle code is here.
How to make a collection
In the backend
Read the previous article: Something is broken?
In the frontend
Remember we used the prototype = true option here because this will allow us to have a prototype version for the tag fields that we can add to our html and then submit. Also remember that until now we did add 3 empty tags in the controller in order to have a non-empty collection. The controller now becomes this as we don’t need to have any tags at first.
In my case, the name for TagType was: khepin_productbundle_producttype_tags (auto generated and I didn’t change it, probably not the best). This means that when I output my form, I have a div with this as an id. And it’s this div that has a ‘data-prototype’ attribute containing the html of the tag fields with a generic ‘$$name$$’ in them that needs to be replaced before inserting into the final html.
We add an ‘Add Tag’ link which when clicked will add a tag field to our html page. In twig new.html.twig:
The add function:
- Finds the div holding our fields for the collection
- Gets the data prototype
- Replaces all instances of $$name$$ in the prototype with a number (current number of elements in the collection. Since they start numbering at 0, we always have one more).
- Adds the resulting html to the div holding the collection
The rest of the script only subscribes the link’s click to activate the “add” function, and prevents the link from actually doing anything.
Now you can add an infinite number of tags to your Product and save all of them at the same time!