Skip to content

Commit f665e14

Browse files
committed
feature symfony#3704 [Form] Added documentation for Form Events (csarrazi)
This PR was merged into the 2.3 branch. Discussion ---------- [Form] Added documentation for Form Events | Q | A | ------------- | --- | Doc fix? | no | New docs? | yes | Applies to | >=2.3 | Fixed tickets | Dunno Added initial documentation for Form Events: * List of events * Use cases for each event * Description of the events workflow * Flow charts for illustration * Registering event listeners or subscribers in a form * Describing the data available for each event Commits ------- 98de95a [Form] Added documentation for Form Events
2 parents 127beed + 98de95a commit f665e14

File tree

7 files changed

+404
-19
lines changed

7 files changed

+404
-19
lines changed

Diff for: components/form/form_events.rst

+398
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,398 @@
1+
.. index::
2+
single: Forms; Form Events
3+
4+
Form Events
5+
===========
6+
7+
The Form component provides a structured process to let you customize your
8+
forms, by making use of the :doc:`EventDispatcher </components/event_dispatcher/introduction>`
9+
component. Using form events, you may modify information or fields at
10+
different steps of the workflow: from the population of the form to the
11+
submission of the data from the request.
12+
13+
Registering an event listener is very easy using the Form component.
14+
15+
For example, if you wish to register a function to the
16+
``FormEvents::PRE_SUBMIT`` event, the following code lets you add a field,
17+
depending on the request' values::
18+
19+
// ...
20+
21+
use Symfony\Component\Form\FormEvent;
22+
use Symfony\Component\Form\FormEvents;
23+
24+
$listener = function (FormEvent $event) {
25+
// ...
26+
};
27+
28+
$form = $formFactory->createBuilder()
29+
// add form fields
30+
->addEventListener(FormEvents::PRE_SUBMIT, $listener);
31+
32+
// ...
33+
34+
The Form Workflow
35+
-----------------
36+
37+
The Form Submission Workflow
38+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
39+
40+
.. image:: /images/components/form/general_flow.png
41+
:align: center
42+
43+
1) Pre-populating the Form (``FormEvents::PRE_SET_DATA`` and ``FormEvents::POST_SET_DATA``)
44+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
45+
46+
.. image:: /images/components/form/set_data_flow.png
47+
:align: center
48+
49+
Two events are dispatched during pre-population of a form, when
50+
:method:`Form::setData() <Symfony\\Component\\Form\\Form::setData>`
51+
is called: ``FormEvents::PRE_SET_DATA`` and ``FormEvents::POST_SET_DATA``.
52+
53+
A) The ``FormEvents::PRE_SET_DATA`` Event
54+
.........................................
55+
56+
The ``FormEvents::PRE_SET_DATA`` event is dispatched at the beginning of the
57+
``Form::setData()`` method. It can be used to:
58+
59+
* Modify the data given during pre-population;
60+
* Modify a form depending on the pre-populated data (adding or removing fields dynamically).
61+
62+
:ref:`Form Events Information Table<component-form-event-table>`
63+
64+
+-----------------+-----------+
65+
| **Data type** | **Value** |
66+
+-----------------+-----------+
67+
| Model data | ``null`` |
68+
+-----------------+-----------+
69+
| Normalized data | ``null`` |
70+
+-----------------+-----------+
71+
| View data | ``null`` |
72+
+-----------------+-----------+
73+
74+
.. caution::
75+
76+
During ``FormEvents::PRE_SET_DATA``,
77+
:method:`Form::setData() <Symfony\\Component\\Form\\Form::setData>`
78+
is locked and will throw an exception if used. If you wish to modify
79+
data, you should use
80+
:method:`FormEvent::setData() <Symfony\\Component\\Form\\FormEvent::setData>`
81+
instead.
82+
83+
.. sidebar:: ``FormEvents::PRE_SET_DATA`` in the Form component
84+
85+
The ``collection`` form type relies on the
86+
:class:`Symfony\\Component\\Form\\Extension\\Core\\EventListener\\ResizeFormListener`
87+
subscriber, listening to the ``FormEvents::PRE_SET_DATA`` event in order
88+
to reorder the form's fields depending on the data from the pre-populated
89+
object, by removing and adding all form rows.
90+
91+
B) The ``FormEvents::POST_SET_DATA`` Event
92+
..........................................
93+
94+
The ``FormEvents::POST_SET_DATA`` event is dispatched at the end of the
95+
:method:`Form::setData() <Symfony\\Component\\Form\\Form::setData>`
96+
method. This event is mostly here for reading data after having pre-populated
97+
the form.
98+
99+
:ref:`Form Events Information Table<component-form-event-table>`
100+
101+
+-----------------+------------------------------------------------------+
102+
| **Data type** | **Value** |
103+
+-----------------+------------------------------------------------------+
104+
| Model data | Model data injected into ``setData()`` |
105+
+-----------------+------------------------------------------------------+
106+
| Normalized data | Model data transformed using a model transformer |
107+
+-----------------+------------------------------------------------------+
108+
| View data | Normalized data transformed using a view transformer |
109+
+-----------------+------------------------------------------------------+
110+
111+
.. sidebar:: ``FormEvents::POST_SET_DATA`` in the Form component
112+
113+
The :class:`Symfony\\Component\\Form\\Extension\\DataCollector\\EventListener\\DataCollectorListener`
114+
class is subscribed to listen to the ``FormEvents::POST_SET_DATA`` event
115+
in order to collect information about the forms from the denormalized
116+
model and view data.
117+
118+
2) Submitting a Form (``FormEvents::PRE_SUBMIT``, ``FormEvents::SUBMIT`` and ``FormEvents::POST_SUBMIT``)
119+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
120+
121+
.. image:: /images/components/form/submission_flow.png
122+
:align: center
123+
124+
Three events are dispatched when
125+
:method:`Form::handleRequest() <Symfony\\Component\\Form\\Form::handleRequest>`
126+
or :method:`Form::submit() <Symfony\\Component\\Form\\Form::submit>` are
127+
called: ``FormEvents::PRE_SUBMIT``, ``FormEvents::SUBMIT``,
128+
``FormEvents::POST_SUBMIT``.
129+
130+
A) The ``FormEvents::PRE_SUBMIT`` Event
131+
.......................................
132+
133+
The ``FormEvents::PRE_SUBMIT`` event is dispatched at the beginning of the
134+
:method:`Form::submit() <Symfony\\Component\\Form\\Form::submit>` method.
135+
136+
It can be used to:
137+
138+
* Change data from the request, before submitting the data to the form.
139+
* Add or remove form fields, before submitting the data to the form.
140+
141+
:ref:`Form Events Information Table<component-form-event-table>`
142+
143+
+-----------------+------------------------------------------+
144+
| **Data type** | **Value** |
145+
+-----------------+------------------------------------------+
146+
| Model data | Same as in ``FormEvents::POST_SET_DATA`` |
147+
+-----------------+------------------------------------------+
148+
| Normalized data | Same as in ``FormEvents::POST_SET_DATA`` |
149+
+-----------------+------------------------------------------+
150+
| View data | Same as in ``FormEvents::POST_SET_DATA`` |
151+
+-----------------+------------------------------------------+
152+
153+
.. sidebar:: ``FormEvents::PRE_SUBMIT`` in the Form component
154+
155+
The :class:`Symfony\\Component\\Form\\Extension\\Core\\EventListener\\TrimListener`
156+
subscriber subscribes to the ``FormEvents::PRE_SUBMIT`` event in order to
157+
trim the request's data (for string values).
158+
The :class:`Symfony\\Component\\Form\\Extension\\Csrf\\EventListener\\CsrfValidationListener`
159+
subscriber subscribes to the ``FormEvents::PRE_SUBMIT`` event in order to
160+
validate the CSRF token.
161+
162+
B) The ``FormEvents::SUBMIT`` Event
163+
...................................
164+
165+
The ``FormEvents::SUBMIT`` event is dispatched just before the
166+
:method:`Form::submit() <Symfony\\Component\\Form\\Form::submit>` method
167+
transforms back the normalized data to the model and view data.
168+
169+
It can be used to change data from the normalized representation of the data.
170+
171+
:ref:`Form Events Information Table<component-form-event-table>`
172+
173+
+-----------------+-------------------------------------------------------------------------------------+
174+
| **Data type** | **Value** |
175+
+-----------------+-------------------------------------------------------------------------------------+
176+
| Model data | Same as in ``FormEvents::POST_SET_DATA`` |
177+
+-----------------+-------------------------------------------------------------------------------------+
178+
| Normalized data | Data from the request reverse-transformed from the request using a view transformer |
179+
+-----------------+-------------------------------------------------------------------------------------+
180+
| View data | Same as in ``FormEvents::POST_SET_DATA`` |
181+
+-----------------+-------------------------------------------------------------------------------------+
182+
183+
.. caution::
184+
185+
At this point, you cannot add or remove fields to the form.
186+
187+
.. sidebar:: ``FormEvents::SUBMIT`` in the Form component
188+
189+
The :class:`Symfony\\Component\\Form\\Extension\\Core\\EventListener\\ResizeFormListener`
190+
subscribes to the ``FormEvents::SUBMIT`` event in order to remove the
191+
fields that need to be removed whenever manipulating a collection of forms
192+
for which ``allow_delete`` has been enabled.
193+
194+
C) The ``FormEvents::POST_SUBMIT`` Event
195+
........................................
196+
197+
The ``FormEvents::POST_SUBMIT`` event is dispatched after the
198+
:method:`Form::submit() <Symfony\\Component\\Form\\Form::submit>` once the
199+
model and view data have been denormalized.
200+
201+
It can be used to fetch data after denormalization.
202+
203+
:ref:`Form Events Information Table<component-form-event-table>`
204+
205+
+-----------------+---------------------------------------------------------------+
206+
| **Data type** | **Value** |
207+
+-----------------+---------------------------------------------------------------+
208+
| Model data | Normalized data reverse-transformed using a model transformer |
209+
+-----------------+---------------------------------------------------------------+
210+
| Normalized data | Same as in ``FormEvents::POST_SUBMIT`` |
211+
+-----------------+---------------------------------------------------------------+
212+
| View data | Normalized data transformed using a view transformer |
213+
+-----------------+---------------------------------------------------------------+
214+
215+
.. caution::
216+
217+
At this point, you cannot add or remove fields to the form.
218+
219+
.. sidebar:: ``FormEvents::POST_SUBMIT`` in the Form component
220+
221+
The :class:`Symfony\\Component\\Form\\Extension\\DataCollector\\EventListener\\DataCollectorListener`
222+
subscribes to the ``FormEvents::POST_SUBMIT`` event in order to collect
223+
information about the forms.
224+
The :class:`Symfony\\Component\\Form\\Extension\\Validator\\EventListener\\ValidationListener`
225+
subscribes to the ``FormEvents::POST_SUBMIT`` event in order to
226+
automatically validate the denormalized object, and update the normalized
227+
as well as the view's representations.
228+
229+
Registering Event Listeners or Event Subscribers
230+
------------------------------------------------
231+
232+
In order to be able to use Form events, you need to create an event listener
233+
or an event subscriber, and register it to an event.
234+
235+
The name of each of the "form" events is defined as a constant on the
236+
:class:`Symfony\\Component\\Form\\FormEvents` class.
237+
Additionally, each event callback (listener or subscriber method) is passed a
238+
single argument, which is an instance of
239+
:class:`Symfony\\Component\\Form\\FormEvent`. The event object contains a
240+
reference to the current state of the form, and the current data being
241+
processed.
242+
243+
.. _component-form-event-table:
244+
245+
+--------------------+-------------------------------+------------------+
246+
| **Name** | ``FormEvents`` **Constant** | **Event's data** |
247+
+--------------------+-------------------------------+------------------+
248+
| form.pre_set_data | ``FormEvents::PRE_SET_DATA`` | Model data |
249+
+--------------------+-------------------------------+------------------+
250+
| form.post_set_data | ``FormEvents::POST_SET_DATA`` | Model data |
251+
+--------------------+-------------------------------+------------------+
252+
| form.pre_bind | ``FormEvents::PRE_SUBMIT`` | Request data |
253+
+--------------------+-------------------------------+------------------+
254+
| form.bind | ``FormEvents::SUBMIT`` | Normalized data |
255+
+--------------------+-------------------------------+------------------+
256+
| form.post_bind | ``FormEvents::POST_SUBMIT`` | View data |
257+
+--------------------+-------------------------------+------------------+
258+
259+
.. versionadded:: 2.3
260+
261+
Before Symfony 2.3, ``FormEvents::PRE_SUBMIT``, ``FormEvents::SUBMIT``
262+
and ``FormEvents::POST_SUBMIT`` were called ``FormEvents::PRE_BIND``,
263+
``FormEvents::BIND`` and ``FormEvents::POST_BIND``.
264+
265+
.. caution::
266+
267+
The ``FormEvents::PRE_BIND``, ``FormEvents::BIND`` and
268+
``FormEvents::POST_BIND`` constants will be removed in version 3.0 of
269+
Symfony.
270+
The event names still keep their original values, so make sure you use the
271+
``FormEvents`` constants in your code for forward compatibility.
272+
273+
Event Listeners
274+
~~~~~~~~~~~~~~~
275+
276+
An event listener may be any type of valid callable.
277+
278+
Creating and binding an event listener to the form is very easy::
279+
280+
// ...
281+
282+
use Symfony\Component\Form\FormEvent;
283+
use Symfony\Component\Form\FormEvents;
284+
285+
$form = $formFactory->createBuilder()
286+
->add('username', 'text')
287+
->add('show_email', 'checkbox')
288+
->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) {
289+
$user = $event->getData();
290+
$form = $event->getForm();
291+
292+
if (!$user) {
293+
return;
294+
}
295+
296+
// Check whether the user has chosen to display his email or not.
297+
// If the data was submitted previously, the additional value that is
298+
// included in the request variables needs to be removed.
299+
if (true === $user['show_email']) {
300+
$form->add('email', 'email');
301+
} else {
302+
unset($user['email']);
303+
$event->setData($user);
304+
}
305+
})
306+
->getForm();
307+
308+
// ...
309+
310+
When you have created a form type class, you can use one of its methods as a
311+
callback for better readability::
312+
313+
// ...
314+
315+
class SubscriptionType extends AbstractType
316+
{
317+
public function buildForm(FormBuilderInterface $builder, array $options)
318+
{
319+
$builder->add('username', 'text');
320+
$builder->add('show_email', 'checkbox');
321+
$builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
322+
}
323+
324+
public function onPreSetData(FormEvent $event)
325+
{
326+
// ...
327+
}
328+
}
329+
330+
Event Subscribers
331+
~~~~~~~~~~~~~~~~~
332+
333+
Event subscribers have different uses:
334+
335+
* Improving readability;
336+
* Listening to multiple events;
337+
* Regrouping multiple listeners inside a single class.
338+
339+
.. code-block:: php
340+
341+
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
342+
use Symfony\Component\Form\FormEvent;
343+
use Symfony\Component\Form\FormEvents;
344+
345+
class AddEmailFieldListener implements EventSubscriberInterface
346+
{
347+
public function getSubscribedEvents()
348+
{
349+
return array(
350+
FormEvents::PRE_SET_DATA => 'onPreSetData',
351+
FormEvents::PRE_SUBMIT => 'onPreSubmit',
352+
);
353+
}
354+
355+
public function onPreSetData(FormEvent $event)
356+
{
357+
$user = $event->getData();
358+
$form = $event->getForm();
359+
360+
// Check whether the user from the initial data has chosen to
361+
// display his email or not.
362+
if (true === $user->isShowEmail()) {
363+
$form->add('email', 'email');
364+
}
365+
}
366+
367+
public function onPreSubmit(FormEvent $event)
368+
{
369+
$user = $event->getData();
370+
$form = $event->getForm();
371+
372+
if (!$user) {
373+
return;
374+
}
375+
376+
// Check whether the user has chosen to display his email or not.
377+
// If the data was submitted previously, the additional value that
378+
// is included in the request variables needs to be removed.
379+
if (true === $user['show_email']) {
380+
$form->add('email', 'email');
381+
} else {
382+
unset($user['email']);
383+
$event->setData($user);
384+
}
385+
}
386+
}
387+
388+
To register the event subscriber, use the addEventSubscriber() method::
389+
390+
// ...
391+
392+
$form = $formFactory->createBuilder()
393+
->add('username', 'text')
394+
->add('show_email', 'checkbox')
395+
->addEventSubscriber(new AddEmailFieldListener())
396+
->getForm();
397+
398+
// ...

0 commit comments

Comments
 (0)