@@ -78,47 +78,118 @@ system to analyze the data on the object and modify the form based on the
7878Product object's data. In this entry, you'll learn how to add this level of
7979flexibility to your forms.
8080
81- .. _`cookbook-forms-event-subscriber ` :
81+ .. _`cookbook-forms-event-listener ` :
8282
83- AddingAn EventSubscriber To A Form Class
84- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83+ Addingan EventListener to a Form Class
84+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8585
86- So, instead of directly adding that "name" widget via your ``ProductType `` form
87- class, let's delegate the responsibility of creating that particular field
88- to an event subscriber::
86+ So, instead of directly adding that ``name `` widget, the responsibility of
87+ creating that particular field is delegated to an event listener::
8988
9089 // src/Acme/DemoBundle/Form/Type/ProductType.php
9190 namespace Acme\DemoBundle\Form\Type;
9291
9392 // ...
94- use Acme\DemoBundle\Form\EventListener\AddNameFieldSubscriber;
93+ use Symfony\Component\Form\FormEvent;
94+ use Symfony\Component\Form\FormEvents;
9595
9696 class ProductType extends AbstractType
9797 {
9898 public function buildForm(FormBuilderInterface $builder, array $options)
9999 {
100100 $builder->add('price');
101101
102- $builder->addEventSubscriber(new AddNameFieldSubscriber());
102+ $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event) {
103+ // ... adding the name field if needed
104+ });
103105 }
104106
105107 // ...
106108 }
107109
108- .. _`cookbook-forms-inside-subscriber-class` :
109110
110- Inside the Event Subscriber Class
111- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
111+ The goal is to create a ``name `` field *only * if the underlying ``Product ``
112+ object is new (e.g. hasn't been persisted to the database). Based on that,
113+ the event listener might look like the following::
112114
113- The goal is to create a "name" field *only * if the underlying Product object
114- is new (e.g. hasn't been persisted to the database). Based on that, the subscriber
115- might look like the following:
115+ // ...
116+ public function buildForm(FormBuilderInterface $builder, array $options)
117+ {
118+ // ...
119+ $builder->addEventListener(FormEvents::PRE_SET_DATA, function(FormEvent $event){
120+ $product = $event->getData();
121+ $form = $event->getForm();
122+
123+ // check if the Product object is "new"
124+ // If no data is passed to the form, the data is "null".
125+ // This should be considered a new "Product"
126+ if (!$product || null !== $product->getId()) {
127+ $form->add('name', 'text');
128+ }
129+ });
130+ }
116131
117132..versionadded ::2.2
118- The ability to pass a string into:method: `FormInterface::add <Symfony\\ Component\\ Form\\ FormInterface::add> `
133+ The ability to pass a string into
134+ :method: `FormInterface::add <Symfony\\ Component\\ Form\\ FormInterface::add> `
119135 was added in Symfony 2.2.
120136
121- ..code-block ::php
137+ ..note ::
138+ You can of course use any callback type instead of a closure, e.g. a method
139+ call on the ``ProductType `` object itself for better readability::
140+
141+ // ...
142+ class ProductType extends AbstractType
143+ {
144+ public function buildForm(FormBuilderInterface $builder, array $options)
145+ {
146+ // ...
147+ $builder->addEventListener(FormEvents::PRE_SET_DATA, array($this, 'onPreSetData'));
148+ }
149+
150+ public function onPreSetData(FormEvent $event){
151+ // ...
152+ }
153+ }
154+
155+ ..note ::
156+
157+ The ``FormEvents::PRE_SET_DATA `` line actually resolves to the string
158+ ``form.pre_set_data ``.:class: `Symfony\\ Component\\ Form\\ FormEvents `
159+ serves an organizational purpose. It is a centralized location in which
160+ you can find all of the various form events available. You can view the
161+ full list of form events via the
162+ :class: `Symfony\\ Component\\ Form\\ FormEvents ` class.
163+
164+ .. _`cookbook-forms-event-subscriber` :
165+
166+ Adding an Event Subscriber to a Form Class
167+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
168+
169+ For better reusability or if there is some heavy logic in your event listener,
170+ you can also move the logic for creating the ``name `` field to an
171+ :ref: `event subscriber <event_dispatcher-using-event-subscribers:ref: >`::
172+
173+ // src/Acme/DemoBundle/Form/Type/ProductType.php
174+ namespace Acme\DemoBundle\Form\Type;
175+
176+ // ...
177+ use Acme\DemoBundle\Form\EventListener\AddNameFieldSubscriber;
178+
179+ class ProductType extends AbstractType
180+ {
181+ public function buildForm(FormBuilderInterface $builder, array $options)
182+ {
183+ $builder->add('price');
184+
185+ $builder->addEventSubscriber(new AddNameFieldSubscriber());
186+ }
187+
188+ // ...
189+ }
190+
191+ Now the logic for creating the ``name `` field resides in it own subscriber
192+ class::
122193
123194 // src/Acme/DemoBundle/Form/EventListener/AddNameFieldSubscriber.php
124195 namespace Acme\DemoBundle\Form\EventListener;
@@ -138,29 +209,15 @@ might look like the following:
138209
139210 public function preSetData(FormEvent $event)
140211 {
141- $data = $event->getData();
212+ $product = $event->getData();
142213 $form = $event->getForm();
143214
144- // check if the product object is "new"
145- // If you didn't pass any data to the form, the data is "null".
146- // This should be considered a new "Product"
147- if (!$data || !$data->getId()) {
215+ if (!$product || null !== $product->getId()) {
148216 $form->add('name', 'text');
149217 }
150218 }
151219 }
152220
153- ..tip ::
154-
155- The ``FormEvents::PRE_SET_DATA `` line actually resolves to the string
156- ``form.pre_set_data ``.:class: `Symfony\\ Component\\ Form\\ FormEvents ` serves
157- an organizational purpose. It is a centralized location in which you can
158- find all of the various form events available.
159-
160- ..note ::
161-
162- You can view the full list of form events via the:class: `Symfony\\ Component\\ Form\\ FormEvents `
163- class.
164221
165222.. _cookbook-form-events-user-data :
166223