Forms in Symfony2: dependent selectboxes

My wish was simple: I wanted to have an extra dropdown box, to be able to filter a table, so the second dropdown box would have less items. So the second dropdown box depends on the first. I found two nice articles about this, but I missed a few things before I got it to work. This post tries to describe what my pitfalls were.

The great articles I found were: Symfony2.4: Dependent Forms and Symfony2 – Dynamic forms, an event-driven approach

The difference with my approach is: building the lists of the selectboxes should be inside the form builder. This way I think it’s more reusable and code is in one place. I don’t want to write extra methods in my controller to fill the selectboxes with javascript. My idea was just to submit the form, and let the form figure out what it should do: save the object, or fill the second list with options.

To describe my pitfall’s, I better first describe my situation. I changed the use case for the sake of this article, but it’s the same with my problem. Suppose you have a Person who can own several cars. On the person edit page, where you can edit his name and other properties, I wanted to add a table with the cars he owns. Under this table I have action buttons for New, Edit and Delete. When you use new, the div on that page is reloaded with the form, so the page isn’t reloaded, you are still on the edit Person page.

This would be my database model:

Forms in Symfony2: model used in this example

The list of cars with all their types would be huge, so I wanted to select the Brand (Opel, Mercedes, BMW) first. This wasn’t a value that I should save to my Person_has_Car model, so I set mapped to false. This was my main problem, because now this value wouldn’t be mapped to the entity, so how should I read it?

Initially I wanted to post the BrandId to the form, and based on this BrandId I wanted to build the select. I found that the PRE_SUBMIT and POST_SUBMIT weren’t called when you don’t submit the entire form. Probably because of CSFR that doesn’t match.

Then after a long search I found that at the PRE_SUBMIT Form Event, the data was just an array instead of an object.

Well, after a few days of frustration, googling, trying, googling and trying, this is what I came up with. Maybe it’s not the best approach, but it seems to work.

So Image that you are on a “User edit” page, and you want to add a car to his account.

Form

My form class PersonHasCarType would be something like this:

Twig template

The (simplified) twig form would look something like this:

The $.parseHtmlBlock is just a little code snippet that replaces HTML based on a json response. It also does some initialisation. The snippet looks like this:

Note the clearOnChange in the javascript that clears the second selectbox (if it exists) with empty values. If I don’t do this, the form has validation errors the second time you change the brand of the car. This was an easy hack to prevent this, and also I think it’s actually not that bad to reset invalid fields.

Controller

Now my controller is something like this:

So basically we now have a form that submits itself whenever the first selectbox changes. The controller checks if the form is fully entered by checking if the car object is set. This approach will probably conflict if you use validation. But for me this seems to work in my application.

2 thoughts on “Forms in Symfony2: dependent selectboxes

    • I think the post is already pretty comprehensive. This site was ment as a place where I could write down solutions to problems that I encountered. I thought I might as well share my thoughts.

      So far I haven’t created full projects to share. I can’t share the complete code because it’s used by my employer. If you tell us what your problem is, we (me, and maybe other readers) may be able to help you think and give you directions.

      But I will not spend hours to publish a full code stack because I can spend those hours much better :-).

Leave a Reply

Your email address will not be published. Required fields are marked *