2011S

Form Composition in Symfony2

UPDATE: This article was written while trying the form system and represents my thoughts at a certain stage, in order to discuss and get feedback. Not all info here is valuable and some is actually mis-leading. If you read this, make sure you read the comments as well, or wait for a coming article with more correct foundations.

Reading these articles in a series (followed by this one) will get you through most of the thought process, and help understand the reasons behind the good practices. Be sure to also read the comments discussion.

As I’m currently trying to get some time to learn symfony2, I went through some basic things in the form framework and found some things I’m not sure how to do. The form framework now seems incredibly powerful though, so I probably missed some things on “how to do” the things I’m trying to do.

What I want to build

I decided that for this first shot with symfony2 I wanted to build a small Facebook application. And as I like to play golf, my application is going to be about it.

I want an application that will publish to my Facebook feed that I played golf on that day, at that place, and what my score was. For this I need to be able to create golf courses (these hold very basic information: name and city). Then I need to add the holes of that golf course. A golf course can have (usually) 9 or 18 holes. Just after I create the course (name, city and number of holes), I want to add the standard scores for each hole. This is essential to later calculate how well a player did on that course.

I created an entity called “Hole” that is linked to a golf course and holds it’s order number (is it the first, seventh of thirteenth hole) and the standard score (the par in golf terms). So when I add holes for the new course, I want the form to display either 9 or 18 Hole form.

How I could do it

First I tried to build everything inside the controller. This form will be used in only one place, there won’t be much customization about it, I don’t really want to create a special form class just for it.

So basically I have one loop in which I had 9 or 18 “HoleType” to my form builder. And I add the 9 to 18 corresponding holes which already hold some data to an array. Then by setting this array as the data for the form and provided that the keys of the array are the same as the field names we gave while building the form, we pre-fill our form.

What I don’t like in this method is that I couldn’t find a way to pre-fill each sub-form while adding it to the global form. I add all the sub-forms first, and then set the data, so I’m going to loop over each sub-form once again to set its default data.

I would have liked to be able to do something like:

This to me looks much more clean and avoids looping over your fields all the time.

The “add” method of the formbuilder accepts a third argument that is an “options” array. In my code example here, I kind of suggest to put the data object as the third argument, maybe one of the possible options is to set the data, but I couldn’t find anything about that.

DI to the rescue!

As I was writing this post, I realized that setting the data in the HoleType controller is possible. Type (or Form) classes don’t inherit from anything, they only implement an abstract class. So everything is up to you on how you create those forms! So if you want to give your HoleType access to a Hole entity, you can! You can do it either through the constructor or through a “setHole()” method.

So if I change my “HoleType” class like this:

I can use my code just like I suggested in my previous example.

I’m still not happy!

I would like all those hole forms to be grouped under a special name in my form to allow me for easier display in the template. Right now the form contains 10 or 19 fields: all the holes (actually each of them contains 3 fields, but that’s not my point), plus a csrf token for security. I would like all the hole fields to be grouped under a larger ‘holes’ property of my form. Then in the template I could do something like:

This way I can deal with bigger parts of my form separately for the display. I can focus on displaying just what I want and have everything like the csrf token taken care of automatically at the end. The only way I found to do this for now was to create a bigger HoleSetType in which I have my 9 or 18 holes, then I can add it to the form with:

Here again, we inject the Course object to the HoleSetType, which then can build the form anyway it needs, adding 9 or 18 holes depending on what the Course is supposed to have. Also all the default data can be added within that class without having to deal with it in the controller.

The way I would love it is to be able to have a GroupFieldType for example to which I could add as many fields as I want on the fly and then add it to the form builder with $form->add(‘my_group’, $group_field);

I have not found a way to do this. I’ve tried using a second form builder to first add all of the holes and then send this under a different name. If anyone has any idea on how this can be done, I’m interested!