[How2Tips] How to modify a Symfony Form based on what the user submitted?

The official docs explain how to do that, but it seems a recurrent problem for many developers to get it right.

The problem

We want to add or modify a field based on the input of a first field.
Example: user choses a country, and based on that country we display a list of cities in that country.

Also, forms don’t allow to be modified once submitted.
PRE_SUBMIT events only contain scalar data, forcing us to resolve the scalar value manually.
This is a waste of resource because the exact same resolution is done automatically during SUBMIT lifecycle.

The solution

We leverage form events and child forms.
Since forms are submitted in a bottom top approach, a child form is submitted before its parent.
By listening the SUBMIT event on a child, we can get its parent and modify it during this event.

Example

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('country', 'choice', [
              'choice_label' => 'name',
              'choice_value' => 'id',
              'choices_as_values' => true,
              'choices' => $this->countryRepository->getCountries(), // or any other source really
              'constraints' => [new Assert\NotBlank],
        ])
    ;
 
    // listen on the **child** form
    $builder->get('country')->addEventListener(FormEvents::SUBMIT, [$this, 'addCities']);
}

public function addCities(FormEvent $event)
{
    $form = $event->getForm()->getParent(); // modify the **parent** form
    // during SUBMIT event, ->getData() actually is the resolved object
    $data = $event->getData();
    if (empty($data)) {
       return;
    }
    $form->add('city', 'choice', [
        'choice_label' => 'name',
        'choice_value' => 'id',
        'choices_as_values' => true,
        'choices' => $data->getCities(),
        'constraints' => [new Assert\NotBlank],
    ]);
}

Result

The first time, the form only contains the country list.
Once submitted, the form will be redisplayed with only the cities of the selected country.
The form will be valid once all the fields are non blank.
No need to validate the choice value, it is done automatically by the choice type.

This article is from our internal knowledge base, we wanted to share it with you. ❤ Send us a KUDO-Tweet if this article has helped you !