Symfony2.4: Dependent Forms

I wanted to update my post about Dependent Selects because there is some deprecated code in it and I also wanted to write a post in English, so here we are! We will see how to implement Dependent Forms using Form events in detail.

We are going to implement the typical Country, Province and City example:

 Symfony2.4: Dependent Forms

Location is the model where we have the a City attribute:

Before we write the Form Type, we have to think about what we want to do. We want a Form Type with 4 fields, address will be a text field, city will be an entity field and the available choices will depend on the province field, this province will depend on country. When we have to add fields to a form which depend on other fields we have to use Form Events. Let’s see the implementation.

Our LocationType looks like this:

As we said before, we have used listeners that will add city, province and country dynamically. We will see the implementation later, but first, we have to understand how Form Events work. I highly recommend you to read the Symfony documentation about dynamic form.

So, which events do we have to listen to?

We can find two scenarios, the first one is when we have to modify the form based on the underlying data, that is when we have our form populated with our model. And the other one is when the form should change depending on the data sent from the user, that is when the form is populated with the submitted data.

So we need our listeners to listen to PRE_SET_DATA and PRE_SUBMIT events. These two events are called just before populating the form (PRE_SET_DATA with the model data and PRE_SUBMIT with the submitted data). Our AddCityFieldSubscriber looks like:

In the preSetData method, the data we receive is from the model, so it will be an instance of the model, Location in this case. We use PropertyAccess because in Location, city is a public attribute, but if we want to use this listener with an Entity for example, we would need to call $data->getCity()  instead of $data->city .

In the preSubmit method we receive the data from the view as an array.

Finally, we add the city field and depending on the value of province, it should only display the cities associated with the selected province.

The AddProvinceFieldSubscriber looks like:

This is similar to the previous Listener, depending on the value of the Country, it displays the provinces associated with the selected Country. We don’t really want the province value in our model, so the mapped attributed is set to false and because of that, we need to pass the Province object to the addProvinceForm in order to show the selected province.

And finally AddCountryFieldSubscriber:

It is like AddProvinceFieldSubscriber, but Country doesn’t depend on any field.

This would be what we need to do with forms. We will also need two actions in a controller to fetch the list of provinces associated to a country and the list of cities associated to a province.

And in the view we will need JavaScript for making those calls when one of the selects change, this would be an example of the JavaScript to update the cities select when the Province select changes:

You can see the fully working example (with the code) here:

Ver Demo