Form Events

Form Events

The Form component provides a structured process to let you customize your forms, by making use of the EventDispatcher component. Using form events, you may modify information or fields at different steps of the workflow: from the population of the form to the submission of the data from the request.

For example, if you need to add a field depending on request values, you can register an event listener to the FormEvents::PRE_SUBMIT event as follows:

  1. // ...
  2. use Symfony\Component\Form\FormEvent;
  3. use Symfony\Component\Form\FormEvents;
  4. $listener = function (FormEvent $event) {
  5. // ...
  6. };
  7. $form = $formFactory->createBuilder()
  8. // ... add form fields
  9. ->addEventListener(FormEvents::PRE_SUBMIT, $listener);
  10. // ...

The Form Workflow

1) Pre-populating the Form (FormEvents::PRE_SET_DATA and FormEvents::POST_SET_DATA)

Two events are dispatched during pre-population of a form, when [Form::setData()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::setData()") is called: FormEvents::PRE_SET_DATA and FormEvents::POST_SET_DATA.

A) The FormEvents::PRE_SET_DATA Event

The FormEvents::PRE_SET_DATA event is dispatched at the beginning of the Form::setData() method. It can be used to:

  • Modify the data given during pre-population;
  • Modify a form depending on the pre-populated data (adding or removing fields dynamically).
Data TypeValue
Model datanull
Normalized datanull
View datanull

See also

See all form events at a glance in the Form Events Information Table.

Caution

During FormEvents::PRE_SET_DATA, [Form::setData()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::setData()") is locked and will throw an exception if used. If you wish to modify data, you should use [FormEvent::setData()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/FormEvent.php "Symfony\Component\Form\FormEvent::setData()") instead.

FormEvents::PRE_SET_DATA in the Form component

The Symfony\Component\Form\Extension\Core\Type\CollectionType form type relies on the Symfony\Component\Form\Extension\Core\EventListener\ResizeFormListener subscriber, listening to the FormEvents::PRE_SET_DATA event in order to reorder the form’s fields depending on the data from the pre-populated object, by removing and adding all form rows.

B) The FormEvents::POST_SET_DATA Event

The FormEvents::POST_SET_DATA event is dispatched at the end of the [Form::setData()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::setData()") method. This event is mostly here for reading data after having pre-populated the form.

Data TypeValue
Model dataModel data injected into setData()
Normalized dataModel data transformed using a model transformer
View dataNormalized data transformed using a view transformer

See also

See all form events at a glance in the Form Events Information Table.

FormEvents::POST_SET_DATA in the Form component

The Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener class is subscribed to listen to the FormEvents::POST_SET_DATA event in order to collect information about the forms from the denormalized model and view data.

2) Submitting a Form (FormEvents::PRE_SUBMIT, FormEvents::SUBMIT and FormEvents::POST_SUBMIT)

Three events are dispatched when [Form::handleRequest()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::handleRequest()") or [Form::submit()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::submit()") are called: FormEvents::PRE_SUBMIT, FormEvents::SUBMIT, FormEvents::POST_SUBMIT.

A) The FormEvents::PRE_SUBMIT Event

The FormEvents::PRE_SUBMIT event is dispatched at the beginning of the [Form::submit()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::submit()") method.

It can be used to:

  • Change data from the request, before submitting the data to the form;
  • Add or remove form fields, before submitting the data to the form.
Data TypeValue
Model dataSame as in FormEvents::POST_SET_DATA
Normalized dataSame as in FormEvents::POST_SET_DATA
View dataSame as in FormEvents::POST_SET_DATA

See also

See all form events at a glance in the Form Events Information Table.

FormEvents::PRE_SUBMIT in the Form component

The Symfony\Component\Form\Extension\Core\EventListener\TrimListener subscriber subscribes to the FormEvents::PRE_SUBMIT event in order to trim the request’s data (for string values). The Symfony\Component\Form\Extension\Csrf\EventListener\CsrfValidationListener subscriber subscribes to the FormEvents::PRE_SUBMIT event in order to validate the CSRF token.

B) The FormEvents::SUBMIT Event

The FormEvents::SUBMIT event is dispatched right before the [Form::submit()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::submit()") method transforms back the normalized data to the model and view data.

It can be used to change data from the normalized representation of the data.

Data TypeValue
Model dataSame as in FormEvents::POST_SET_DATA
Normalized dataData from the request reverse-transformed from the request using a view transformer
View dataSame as in FormEvents::POST_SET_DATA

See also

See all form events at a glance in the Form Events Information Table.

Caution

At this point, you cannot add or remove fields to the form.

FormEvents::SUBMIT in the Form component

The Symfony\Component\Form\Extension\Core\EventListener\FixUrlProtocolListener subscribes to the FormEvents::SUBMIT event in order to prepend a default protocol to URL fields that were submitted without a protocol.

C) The FormEvents::POST_SUBMIT Event

The FormEvents::POST_SUBMIT event is dispatched after the [Form::submit()](https://github.com/symfony/symfony/blob/4.4/src/Symfony/Component/Form/Form.php "Symfony\Component\Form\Form::submit()") once the model and view data have been denormalized.

It can be used to fetch data after denormalization.

Data TypeValue
Model dataNormalized data reverse-transformed using a model transformer
Normalized dataSame as in FormEvents::SUBMIT
View dataNormalized data transformed using a view transformer

See also

See all form events at a glance in the Form Events Information Table.

Caution

At this point, you cannot add or remove fields to the current form and its children.

FormEvents::POST_SUBMIT in the Form component

The Symfony\Component\Form\Extension\DataCollector\EventListener\DataCollectorListener subscribes to the FormEvents::POST_SUBMIT event in order to collect information about the forms. The Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener subscribes to the FormEvents::POST_SUBMIT event in order to automatically validate the denormalized object.

Registering Event Listeners or Event Subscribers

In order to be able to use Form events, you need to create an event listener or an event subscriber and register it to an event.

The name of each of the “form” events is defined as a constant on the Symfony\Component\Form\FormEvents class. Additionally, each event callback (listener or subscriber method) is passed a single argument, which is an instance of Symfony\Component\Form\FormEvent. The event object contains a reference to the current state of the form and the current data being processed.

NameFormEvents ConstantEvent’s Data
form.pre_set_dataFormEvents::PRE_SET_DATAModel data
form.post_set_dataFormEvents::POST_SET_DATAModel data
form.pre_submitFormEvents::PRE_SUBMITRequest data
form.submitFormEvents::SUBMITNormalized data
form.post_submitFormEvents::POST_SUBMITView data

Event Listeners

An event listener may be any type of valid callable. For example, you can define an event listener function inline right in the addEventListener method of the FormFactory:

  1. // ...
  2. use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  3. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  4. use Symfony\Component\Form\Extension\Core\Type\TextType;
  5. use Symfony\Component\Form\FormEvent;
  6. use Symfony\Component\Form\FormEvents;
  7. $form = $formFactory->createBuilder()
  8. ->add('username', TextType::class)
  9. ->add('showEmail', CheckboxType::class)
  10. ->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
  11. $user = $event->getData();
  12. $form = $event->getForm();
  13. if (!$user) {
  14. return;
  15. }
  16. // checks whether the user has chosen to display their email or not.
  17. // If the data was submitted previously, the additional value that is
  18. // included in the request variables needs to be removed.
  19. if (isset($user['showEmail']) && $user['showEmail']) {
  20. $form->add('email', EmailType::class);
  21. } else {
  22. unset($user['email']);
  23. $event->setData($user);
  24. }
  25. })
  26. ->getForm();
  27. // ...

When you have created a form type class, you can use one of its methods as a callback for better readability:

  1. // src/Form/SubscriptionType.php
  2. namespace App\Form;
  3. use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  4. use Symfony\Component\Form\Extension\Core\Type\TextType;
  5. use Symfony\Component\Form\FormEvent;
  6. use Symfony\Component\Form\FormEvents;
  7. // ...
  8. class SubscriptionType extends AbstractType
  9. {
  10. public function buildForm(FormBuilderInterface $builder, array $options): void
  11. {
  12. $builder
  13. ->add('username', TextType::class)
  14. ->add('showEmail', CheckboxType::class)
  15. ->addEventListener(
  16. FormEvents::PRE_SET_DATA,
  17. [$this, 'onPreSetData']
  18. )
  19. ;
  20. }
  21. public function onPreSetData(FormEvent $event): void
  22. {
  23. // ...
  24. }
  25. }

Event Subscribers

Event subscribers have different uses:

  • Improving readability;
  • Listening to multiple events;
  • Regrouping multiple listeners inside a single class.

Consider the following example of a form event subscriber:

  1. // src/Form/EventListener/AddEmailFieldListener.php
  2. namespace App\Form\EventListener;
  3. use Symfony\Component\EventDispatcher\EventSubscriberInterface;
  4. use Symfony\Component\Form\Extension\Core\Type\EmailType;
  5. use Symfony\Component\Form\FormEvent;
  6. use Symfony\Component\Form\FormEvents;
  7. class AddEmailFieldListener implements EventSubscriberInterface
  8. {
  9. public static function getSubscribedEvents(): array
  10. {
  11. return [
  12. FormEvents::PRE_SET_DATA => 'onPreSetData',
  13. FormEvents::PRE_SUBMIT => 'onPreSubmit',
  14. ];
  15. }
  16. public function onPreSetData(FormEvent $event): void
  17. {
  18. $user = $event->getData();
  19. $form = $event->getForm();
  20. // checks whether the user from the initial data has chosen to
  21. // display their email or not.
  22. if (true === $user->isShowEmail()) {
  23. $form->add('email', EmailType::class);
  24. }
  25. }
  26. public function onPreSubmit(FormEvent $event): void
  27. {
  28. $user = $event->getData();
  29. $form = $event->getForm();
  30. if (!$user) {
  31. return;
  32. }
  33. // checks whether the user has chosen to display their email or not.
  34. // If the data was submitted previously, the additional value that
  35. // is included in the request variables needs to be removed.
  36. if (isset($user['showEmail']) && $user['showEmail']) {
  37. $form->add('email', EmailType::class);
  38. } else {
  39. unset($user['email']);
  40. $event->setData($user);
  41. }
  42. }
  43. }

To register the event subscriber, use the addEventSubscriber() method:

  1. use App\Form\EventListener\AddEmailFieldListener;
  2. use Symfony\Component\Form\Extension\Core\Type\CheckboxType;
  3. use Symfony\Component\Form\Extension\Core\Type\TextType;
  4. // ...
  5. $form = $formFactory->createBuilder()
  6. ->add('username', TextType::class)
  7. ->add('showEmail', CheckboxType::class)
  8. ->addEventSubscriber(new AddEmailFieldListener())
  9. ->getForm();
  10. // ...

This work, including the code samples, is licensed under a Creative Commons BY-SA 3.0 license.