Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge branch '2.1' into 2.2

Conflicts:
	components/http_foundation/introduction.rst
	cookbook/form/dynamic_form_modification.rst
  • Loading branch information...
commit f2b48c770ee270c9bc4caed86345cefd7eb4f004 2 parents 8e66c04 + c1c4424
@weaverryan weaverryan authored
View
10 book/installation.rst
@@ -172,9 +172,13 @@ Symfony itself - into the ``vendor/`` directory.
When running ``php composer.phar install`` or ``php composer.phar update``,
composer will execute post install/update commands to clear the cache
and install assets. By default, the assets will be copied into your ``web``
- directory. To create symlinks instead of copying the assets, you can
- add an entry in the ``extra`` node of your composer.json file with the
- key ``symfony-assets-install`` and the value ``symlink``:
+ directory.
+
+ Instead of copying your Symfony assets, you can create symlinks if
+ your operating system supports it. To create symlinks, add an entry
+ in the ``extra`` node of your composer.json file with the key
+ ``symfony-assets-install`` and the value ``symlink``:
+
.. code-block:: json
View
9 book/templating.rst
@@ -1344,8 +1344,13 @@ is being escaped for HTML output.
In some cases, you'll need to disable output escaping when you're rendering
a variable that is trusted and contains markup that should not be escaped.
Suppose that administrative users are able to write articles that contain
-HTML code. By default, Twig will escape the article body. To render it normally,
-add the ``raw`` filter: ``{{ article.body|raw }}``.
+HTML code. By default, Twig will escape the article body.
+
+To render it normally, add the ``raw`` filter:
+
+.. code-block:: jinja
+
+ {{ article.body|raw }}
You can also disable output escaping inside a ``{% block %}`` area or
for an entire template. For more information, see `Output Escaping`_ in
View
10 components/http_foundation/introduction.rst
@@ -406,6 +406,16 @@ represented by a PHP callable instead of a string::
});
$response->send();
+.. note::
+
+ The ``flush()`` function does not flush buffering. If ``ob_start()`` has
+ been called before or the ``output_buffering`` php.ini option is enabled,
+ you must call ``ob_flush()`` before ``flush()``.
+
+ Additionally, PHP isn't the only layer that can buffer output. Your web
+ server might also buffer based on its configuration. Even more, if you
+ use fastcgi, buffering can't be disabled at all.
+
.. _component-http-foundation-serving-files:
Serving Files
View
44 components/process.rst
@@ -26,15 +26,16 @@ a command in a sub-process::
$process = new Process('ls -lsa');
$process->setTimeout(3600);
$process->run();
+
+ // executes after the the command finishes
if (!$process->isSuccessful()) {
throw new \RuntimeException($process->getErrorOutput());
}
print $process->getOutput();
-The :method:`Symfony\\Component\\Process\\Process::run` method takes care
-of the subtle differences between the different platforms when executing the
-command.
+The component takes care of the subtle differences between the different platforms
+when executing the command.
.. versionadded:: 2.2
The ``getIncrementalOutput()`` and ``getIncrementalErrorOutput()`` methods were added in Symfony 2.2.
@@ -60,6 +61,41 @@ anonymous function to the
echo 'OUT > '.$buffer;
}
});
+
+.. versionadded:: 2.1
+ The non-blocking feature was added in 2.1.
+
+You can also start the subprocess and then let it run asynchronously, retrieving
+output and the status in your main process whenever you need it. Use the
+:method:`Symfony\\Component\\Process\\Process::start` method to start an asynchronous
+process, the :method:`Symfony\\Component\\Process\\Process::isRunning` method
+to check if the process is done and the
+:method:`Symfony\\Component\\Process\\Process::getOutput` method to get the output::
+
+ $process = new Process('ls -lsa');
+ $process->start();
+
+ while ($process->isRunning()) {
+ // waiting for process to finish
+ }
+
+ echo $process->getOutput();
+
+You can also wait for a process to end if you started it asynchronously and
+are done doing other stuff::
+
+ $process = new Process('ls -lsa');
+ $process->start();
+
+ // do other things
+
+ $process->wait(function ($type, $buffer) {
+ if ('err' === $type) {
+ echo 'ERR > '.$buffer;
+ } else {
+ echo 'OUT > '.$buffer;
+ }
+ });
If you want to execute some PHP code in isolation, use the ``PhpProcess``
instead::
@@ -73,7 +109,7 @@ instead::
$process->run();
.. versionadded:: 2.1
- The ``ProcessBuilder`` class has been as of 2.1.
+ The ``ProcessBuilder`` class was added in Symfony 2.1.
To make your code work better on all platforms, you might want to use the
:class:`Symfony\\Component\\Process\\ProcessBuilder` class instead::
View
2  cookbook/form/create_custom_field_type.rst
@@ -207,6 +207,8 @@ But this only works because the ``GenderType()`` is very simple. What if
the gender codes were stored in configuration or in a database? The next
section explains how more complex field types solve this problem.
+.. _form-cookbook-form-field-service:
+
Creating your Field Type as a Service
-------------------------------------
View
399 cookbook/form/dynamic_form_modification.rst
@@ -2,7 +2,32 @@
single: Form; Events
How to Dynamically Modify Forms Using Form Events
-===================================================
+=================================================
+
+Often times, a form can't be created statically. In this entry, you'll learn
+how to customize your form based on three common use-cases:
+
+1) :ref:`cookbook-form-events-underlying-data`
+
+Example: you have a "Product" form and need to modify/add/remove a field
+based on the data on the underlying Product being edited.
+
+2) :ref:`cookbook-form-events-user-data`
+
+Example: you create a "Friend Message" form and need to build a drop-down
+that contains only users that are friends with the *current* authenticated
+user.
+
+3) :ref:`cookbook-form-events-submitted-data`
+
+Example: on a registration form, you have a "country" field and a "state"
+field which should populate dynamically based on the value in the "country"
+field.
+
+.. _cookbook-form-events-underlying-data:
+
+Customizing your Form based on the underlying Data
+--------------------------------------------------
Before jumping right into dynamic form generation, let's have a quick review
of what a bare form class looks like::
@@ -12,6 +37,7 @@ of what a bare form class looks like::
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
+ use Symfony\Component\OptionsResolver\OptionsResolverInterface;
class ProductType extends AbstractType
{
@@ -21,6 +47,13 @@ of what a bare form class looks like::
$builder->add('price');
}
+ public function setDefaultOptions(OptionsResolverInterface $resolver)
+ {
+ $resolver->setDefaults(array(
+ 'data_class' => 'Acme\DemoBundle\Entity\Product'
+ ));
+ }
+
public function getName()
{
return 'product';
@@ -33,9 +66,9 @@ of what a bare form class looks like::
probably need to take a step back and first review the :doc:`Forms chapter </book/forms>`
before proceeding.
-Let's assume for a moment that this form utilizes an imaginary "Product" class
-that has only two relevant properties ("name" and "price"). The form generated
-from this class will look the exact same regardless if a new Product is being created
+Assume for a moment that this form utilizes an imaginary "Product" class
+that has only two properties ("name" and "price"). The form generated from
+this class will look the exact same regardless if a new Product is being created
or if an existing product is being edited (e.g. a product fetched from the database).
Suppose now, that you don't want the user to be able to change the ``name`` value
@@ -48,7 +81,7 @@ flexibility to your forms.
.. _`cookbook-forms-event-subscriber`:
Adding An Event Subscriber To A Form Class
-------------------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
So, instead of directly adding that "name" widget via your ProductType form
class, let's delegate the responsibility of creating that particular field
@@ -57,8 +90,7 @@ to an Event Subscriber::
// src/Acme/DemoBundle/Form/Type/ProductType.php
namespace Acme\DemoBundle\Form\Type;
- use Symfony\Component\Form\AbstractType;
- use Symfony\Component\Form\FormBuilderInterface;
+ // ...
use Acme\DemoBundle\Form\EventListener\AddNameFieldSubscriber;
class ProductType extends AbstractType
@@ -70,16 +102,13 @@ to an Event Subscriber::
$builder->addEventSubscriber(new AddNameFieldSubscriber());
}
- public function getName()
- {
- return 'product';
- }
+ // ...
}
.. _`cookbook-forms-inside-subscriber-class`:
Inside the Event Subscriber Class
----------------------------------
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The goal is to create a "name" field *only* if the underlying Product object
is new (e.g. hasn't been persisted to the database). Based on that, the subscriber
@@ -112,62 +141,45 @@ might look like the following:
$data = $event->getData();
$form = $event->getForm();
- // During form creation setData() is called with null as an argument
- // by the FormBuilder constructor. You're only concerned with when
- // setData is called with an actual Entity object in it (whether new
- // or fetched with Doctrine). This if statement lets you skip right
- // over the null condition.
- if (null === $data) {
- return;
- }
-
// check if the product object is "new"
- if (!$data->getId()) {
+ // If you didn't pass any data to the form, the data is "null".
+ // This should be considered a new "Product"
+ if (!$data || !$data->getId()) {
$form->add('name', 'text');
}
}
}
-.. caution::
-
- It is easy to misunderstand the purpose of the ``if (null === $data)`` segment
- of this event subscriber. To fully understand its role, you might consider
- also taking a look at the `Form class`_ and paying special attention to
- where setData() is called at the end of the constructor, as well as the
- setData() method itself.
-
-The ``FormEvents::PRE_SET_DATA`` line actually resolves to the string ``form.pre_set_data``.
-The `FormEvents class`_ serves an organizational purpose. It is a centralized location
-in which you can find all of the various form events available.
+.. tip::
-While this example could have used the ``form.post_set_data``
-event just as effectively, by using ``form.pre_set_data`` you guarantee that
-the data being retrieved from the ``Event`` object has in no way been modified
-by any other subscribers or listeners because ``form.pre_set_data`` is the
-first form event dispatched.
+ The ``FormEvents::PRE_SET_DATA`` line actually resolves to the string
+ ``form.pre_set_data``. :class:`Symfony\\Component\\Form\\FormEvents` serves
+ an organizational purpose. It is a centralized location in which you can
+ find all of the various form events available.
.. note::
- You may view the full list of form events via the `FormEvents class`_,
- found in the form bundle.
+ You can view the full list of form events via the :class:`Symfony\\Component\\Form\\FormEvents`
+ class.
-How to Dynamically Generate Forms based on user data
-====================================================
+.. _cookbook-form-events-user-data:
+
+How to Dynamically Generate Forms based on user Data
+----------------------------------------------------
Sometimes you want a form to be generated dynamically based not only on data
-from this form but also on something else. For example depending on the user
-currently using the application. If you have a social website where a user can
-only message people who are his friends on the website, then the current user
-doesn't need to be included as a field of your form, but a "choice list" of
-whom to message should only contain users that are the current user's friends.
+from the form but also on something else - like some data from the current user.
+Suppose you have a social website where a user can only message people who
+are his friends on the website. In this case, a "choice list" of whom to message
+should only contain users that are the current user's friends.
-Creating the form type
-----------------------
+Creating the Form Type
+~~~~~~~~~~~~~~~~~~~~~~
-Using an event listener, our form could be built like this::
+Using an event listener, your form might look like this::
- // src/Acme/DemoBundle/FormType/FriendMessageFormType.php
- namespace Acme\DemoBundle\FormType;
+ // src/Acme/DemoBundle/Form/Type/FriendMessageFormType.php
+ namespace Acme\DemoBundle\Form\Type;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
@@ -175,7 +187,6 @@ Using an event listener, our form could be built like this::
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Security\Core\SecurityContext;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
- use Acme\DemoBundle\FormSubscriber\UserListener;
class FriendMessageFormType extends AbstractType
{
@@ -200,13 +211,11 @@ Using an event listener, our form could be built like this::
}
}
-The problem is now to get the current application user and create a choice field
-that would contain only this user's friends.
+The problem is now to get the current user and create a choice field that
+contains only this user's friends.
Luckily it is pretty easy to inject a service inside of the form. This can be
-done in the constructor.
-
-.. code-block:: php
+done in the constructor::
private $securityContext;
@@ -217,19 +226,26 @@ done in the constructor.
.. note::
- You might wonder, now that we have access to the User (through) the security
- context, why don't we just use that inside of the buildForm function and
- still use a listener?
- This is because doing so in the buildForm method would result in the whole
- form type being modified and not only one form instance.
+ You might wonder, now that you have access to the User (through the security
+ context), why not just use it directly in ``buildForm`` and omit the
+ event listener? This is because doing so in the ``buildForm`` method
+ would result in the whole form type being modified and not just this
+ one form instance. This may not usually be a problem, but technically
+ a single form type could be used on a single request to create many forms
+ or fields.
-Customizing the form type
--------------------------
+Customizing the Form Type
+~~~~~~~~~~~~~~~~~~~~~~~~~
-Now that we have all the basics in place, we can put everything in place and add
-our listener::
+Now that you have all the basics in place you an take advantage of the ``securityContext``
+and fill in the listener logic::
// src/Acme/DemoBundle/FormType/FriendMessageFormType.php
+
+ use Symfony\Component\Security\Core\SecurityContext;
+ use Doctrine\ORM\EntityRepository;
+ // ...
+
class FriendMessageFormType extends AbstractType
{
private $securityContext;
@@ -245,66 +261,98 @@ our listener::
->add('subject', 'text')
->add('body', 'textarea')
;
+
+ // grab the user, do a quick sanity check that one exists
$user = $this->securityContext->getToken()->getUser();
+ if (!$user) {
+ throw new \LogicException(
+ 'The FriendMessageFormType cannot be used without an authenticated user!'
+ );
+ }
+
$factory = $builder->getFormFactory();
$builder->addEventListener(
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use($user, $factory){
$form = $event->getForm();
- $userId = $user->getId();
$formOptions = array(
- 'class' => 'Acme\DemoBundle\Document\User',
+ 'class' => 'Acme\DemoBundle\Entity\User',
'multiple' => false,
'expanded' => false,
'property' => 'fullName',
- 'query_builder' => function(DocumentRepository $dr) use ($userId) {
- return $dr->createQueryBuilder()->field('friends.$id')->equals(new \MongoId($userId));
+ 'query_builder' => function(EntityRepository $er) use ($user) {
+ // build a custom query, or call a method on your repository (even better!)
},
);
- $form->add($factory->createNamed('friend', 'document', null, $formOptions));
+ // create the field, this is similar the $builder->add()
+ // field name, field type, data, options
+ $form->add($factory->createNamed('friend', 'entity', null, $formOptions));
}
);
}
- public function getName()
- {
- return 'acme_friend_message';
- }
+ // ...
+ }
- public function setDefaultOptions(OptionsResolverInterface $resolver)
+Using the Form
+~~~~~~~~~~~~~~
+
+Our form is now ready to use and there are two possible ways to use it inside
+of a controller:
+
+a) create it manually and remember to pass the security context to it;
+
+or
+
+b) define it as a service.
+
+a) Creating the Form manually
+.............................
+
+This is very simple, and is probably the better approach unless you're using
+your new form type in many places or embedding it into other forms::
+
+ class FriendMessageController extends Controller
+ {
+ public function newAction(Request $request)
{
+ $securityContext = $this->container->get('security.context');
+ $form = $this->createForm(
+ new FriendMessageFormType($securityContext)
+ );
+
+ // ...
}
}
-Using the form
---------------
-
-Our form is now ready to use. We have two possible ways to use it inside of a
-controller. Either by creating it everytime and remembering to pass the security
-context, or by defining it as a service. This is the option we will show here.
+b) Defining the Form as a Service
+.................................
-To define your form as a service, you simply add the configuration to your
-configuration.
+To define your form as a service, just create a normal service and then tag
+it with :ref:`dic-tags-form-type`.
.. configuration-block::
.. code-block:: yaml
# app/config/config.yml
- acme.form.friend_message:
- class: Acme\DemoBundle\FormType\FriendMessageType
- arguments: [@security.context]
- tags:
- - { name: form.type, alias: acme_friend_message}
+ services:
+ acme.form.friend_message:
+ class: Acme\DemoBundle\Form\Type\FriendMessageFormType
+ arguments: [@security.context]
+ tags:
+ -
+ name: form.type
+ alias: acme_friend_message
.. code-block:: xml
<!-- app/config/config.xml -->
<services>
- <service id="acme.form.friend_message" class="Acme\DemoBundle\FormType\FriendMessageType">
+ <service id="acme.form.friend_message" class="Acme\DemoBundle\Form\Type\FriendMessageFormType">
<argument type="service" id="security.context" />
<tag name="form.type" alias="acme_friend_message" />
</service>
@@ -313,7 +361,7 @@ configuration.
.. code-block:: php
// app/config/config.php
- $definition = new Definition('Acme\DemoBundle\FormType\FriendMessageType');
+ $definition = new Definition('Acme\DemoBundle\Form\Type\FriendMessageFormType');
$definition->addTag('form.type', array('alias' => 'acme_friend_message'));
$container->setDefinition(
'acme.form.friend_message',
@@ -321,39 +369,44 @@ configuration.
array('security.context')
);
-By adding the form as a service, we make sure that this form can now be used
-simply from anywhere. If you need to add it to another form, you will just need
-to use::
-
- $builder->add('message', 'acme_friend_message');
-
If you wish to create it from within a controller or any other service that has
access to the form factory, you then use::
- // src/AcmeDemoBundle/Controller/FriendMessageController.php
- public function friendMessageAction()
+ class FriendMessageController extends Controller
{
- $form = $this->get('form.factory')->create('acme_friend_message');
- $form = $form->createView();
+ public function newAction(Request $request)
+ {
+ $form = $this->createForm('acme_friend_message');
+
+ // ...
+ }
+ }
- return compact('form');
+You can also easily embed the form type into another form::
+
+ // inside some other "form type" class
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ $builder->add('message', 'acme_friend_message');
}
-Dynamic generation for submitted forms
-======================================
+.. _cookbook-form-events-submitted-data:
+
+Dynamic generation for submitted Forms
+--------------------------------------
-An other case that can appear is that you want to customize the form specific to
-the data that was submitted by the user. If we take as an example a registration
+Another case that can appear is that you want to customize the form specific to
+the data that was submitted by the user. For example, imagine you have a registration
form for sports gatherings. Some events will allow you to specify your preferred
-position on the field. This would be a choice field for example. However the
+position on the field. This would be a ``choice`` field for example. However the
possible choices will depend on each sport. Football will have attack, defense,
-goalkeeper etc... Baseball will have a pitcher but will not have goalkeeper. We
+goalkeeper etc... Baseball will have a pitcher but will not have goalkeeper. You
will need the correct options to be set in order for validation to pass.
The meetup is passed as an entity hidden field to the form. So we can access each
sport like this::
- // src/Acme/DemoBundle/FormType/SportMeetupType.php
+ // src/Acme/DemoBundle/Form/Type/SportMeetupType.php
class SportMeetupType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -368,7 +421,11 @@ sport like this::
FormEvents::PRE_SET_DATA,
function(FormEvent $event) use($user, $factory){
$form = $event->getForm();
- $event->getData()->getSport()->getAvailablePositions();
+
+ // this would be your entity, i.e. SportMeetup
+ $data = $event->getData();
+
+ $positions = $data->getSport()->getAvailablePositions();
// ... proceed with customizing the form based on available positions
}
@@ -376,35 +433,42 @@ sport like this::
}
}
+When you're building this form to display to the user for the first time,
+then this example works perfectly.
-While generating this kind of form to display it to the user for the first time,
-we can just as previously, use a simple listener and all goes fine.
+However, things get more difficult when you handle the form submission. This
+is be cause the ``PRE_SET_DATA`` event tells us the data that you're starting
+with (e.g. an empty ``SportMeetup`` object), *not* the submitted data.
-When considering form submission, things are usually a bit different because
-subscribing to PRE_SET_DATA will only return us an empty ``SportMeetup`` object.
-That object will then be populated with the data sent by the user when there is a
-call to ``$form->bind($request)``.
+On a form, we can usually listen to the following events:
-On a form, we can usually listen to the following events::
+* ``PRE_SET_DATA``
+* ``POST_SET_DATA``
+* ``PRE_BIND``
+* ``BIND``
+* ``POST_BIND``
- * ``PRE_SET_DATA``
- * ``POST_SET_DATA``
- * ``PRE_BIND``
- * ``BIND``
- * ``POST_BIND``
+When listening to ``BIND`` and ``POST_BIND``, it's already "too late" to make
+changes to the form. Fortunately, ``PRE_BIND`` is perfect for this. There
+is, however, a big difference in what ``$event->getData()`` returns for each
+of these events. Specifically, in ``PRE_BIND``, ``$event->getData()`` returns
+the raw data submitted by the user.
-When listening to bind and post-bind, it's already "too late" to make changes to
-the form. But pre-bind is fine. There is however a big difference in what
-``$event->getData()`` will return for each of these events as pre-bind will return
-an array instead of an object. This is the raw data submitted by the user.
+This can be used to get the ``SportMeetup`` id and retrieve it from the database,
+given you have a reference to the object manager (if using doctrine). In
+the end, you have an event subscriber that listens to two different events,
+requires some external services and customizes the form. In such a situation,
+it's probably better to define this as a service rather than using an anonymouse
+function as the event listener callback.
-This can be used to get the SportMeetup's id and retrieve it from the database,
-given we have a reference to our object manager (if using doctrine). So we have
-an event subscriber that listens to two different events, requires some
-external services and customizes our form. In such a situation, it seems cleaner
-to define this as a service rather than use closure like in the previous example.
+The subscriber would now look like::
-Our subscriber would now look like::
+ // src/Acme/DemoBundle/Form/EventListener/RegistrationSportListener.php
+ namespace Acme\DemoBundle\Form\EventListener;
+
+ use Symfony\Component\Form\FormFactoryInterface;
+ use Doctrine\ORM\EntityManager;
+ use Symfony\Component\Form\FormEvent;
class RegistrationSportListener implements EventSubscriberInterface
{
@@ -414,14 +478,14 @@ Our subscriber would now look like::
private $factory;
/**
- * @var DocumentManager
+ * @var EntityManager
*/
private $om;
/**
* @param factory FormFactoryInterface
*/
- public function __construct(FormFactoryInterface $factory, ObjectManager $om)
+ public function __construct(FormFactoryInterface $factory, EntityManager $om)
{
$this->factory = $factory;
$this->om = $om;
@@ -429,16 +493,16 @@ Our subscriber would now look like::
public static function getSubscribedEvents()
{
- return [
+ return array(
FormEvents::PRE_BIND => 'preBind',
FormEvents::PRE_SET_DATA => 'preSetData',
- ];
+ );
}
/**
- * @param event DataEvent
+ * @param event FormEvent
*/
- public function preSetData(DataEvent $event)
+ public function preSetData(FormEvent $event)
{
$meetup = $event->getData()->getMeetup();
@@ -448,19 +512,20 @@ Our subscriber would now look like::
}
$form = $event->getForm();
- $positions = $meetup->getSport()->getPostions();
+ $positions = $meetup->getSport()->getPositions();
$this->customizeForm($form, $positions);
}
- public function preBind(DataEvent $event)
+ public function preBind(FormEvent $event)
{
$data = $event->getData();
$id = $data['event'];
$meetup = $this->om
- ->getRepository('Acme\SportBundle\Document\Event')
- ->find($id);
- if($meetup === null){
+ ->getRepository('AcmeDemoBundle:SportMeetup')
+ ->find($id);
+
+ if ($meetup === null) {
$msg = 'The event %s could not be found for you registration';
throw new \Exception(sprintf($msg, $id));
}
@@ -476,12 +541,12 @@ Our subscriber would now look like::
}
}
-We can see that we need to listen on these two events and have different callbacks
-only because in two different scenarios, the data that we can use is given in a
+You can see that you need to listen on these two events and have different callbacks
+only because in two different scenarios, the data that you can use is given in a
different format. Other than that, this class always performs exactly the same
things on a given form.
-Now that we have this set up, we need to create our services:
+Now that you have that setup, register your form and the listener as services:
.. configuration-block::
@@ -489,23 +554,23 @@ Now that we have this set up, we need to create our services:
# app/config/config.yml
acme.form.sport_meetup:
- class: Acme\SportBundle\FormType\RegistrationType
+ class: Acme\SportBundle\Form\Type\SportMeetupType
arguments: [@acme.form.meetup_registration_listener]
tags:
- { name: form.type, alias: acme_meetup_registration }
acme.form.meetup_registration_listener
- class: Acme\SportBundle\Form\RegistrationSportListener
+ class: Acme\SportBundle\Form\EventListener\RegistrationSportListener
arguments: [@form.factory, @doctrine]
.. code-block:: xml
<!-- app/config/config.xml -->
<services>
- <service id="acme.form.sport_meetup" class="Acme\SportBundle\FormType\RegistrationType">
+ <service id="acme.form.sport_meetup" class="Acme\SportBundle\FormType\SportMeetupType">
<argument type="service" id="acme.form.meetup_registration_listener" />
<tag name="form.type" alias="acme_meetup_registration" />
</service>
- <service id="acme.form.meetup_registration_listener" class="Acme\SportBundle\Form\RegistrationSportListener">
+ <service id="acme.form.meetup_registration_listener" class="Acme\SportBundle\Form\EventListener\RegistrationSportListener">
<argument type="service" id="form.factory" />
<argument type="service" id="doctrine" />
</service>
@@ -514,24 +579,44 @@ Now that we have this set up, we need to create our services:
.. code-block:: php
// app/config/config.php
- $definition = new Definition('Acme\SportBundle\FormType\RegistrationType');
+ $definition = new Definition('Acme\SportBundle\Form\Type\SportMeetupType');
$definition->addTag('form.type', array('alias' => 'acme_meetup_registration'));
$container->setDefinition(
'acme.form.meetup_registration_listener',
$definition,
array('security.context')
);
- $definition = new Definition('Acme\SportBundle\Form\RegistrationSportListener');
+ $definition = new Definition('Acme\SportBundle\Form\EventListener\RegistrationSportListener');
$container->setDefinition(
'acme.form.meetup_registration_listener',
$definition,
array('form.factory', 'doctrine')
);
-And this should tie everything together. We can now retrieve our form from the
+In this setup, the ``RegistrationSportListener`` will be a constructor argument
+to ``SportMeetupType``. You can then register it as an event subscriber on
+your form::
+
+ private $registrationSportListener;
+
+ public function __construct(RegistrationSportListener $registrationSportListener)
+ {
+ $this->registrationSportListener = $registrationSportListener;
+ }
+
+ public function buildForm(FormBuilderInterface $builder, array $options)
+ {
+ // ...
+ $subscriber = new AddNameFieldSubscriber($builder->getFormFactory());
+ $builder->addEventSubscriber($this->registrationSportListener);
+ }
+
+And this should tie everything together. You can now retrieve your form from the
controller, display it to a user, and validate it with the right choice options
set for every possible kind of sport that our users are registering for.
-.. _`DataEvent`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Event/DataEvent.php
-.. _`FormEvents class`: https://github.com/symfony/Form/blob/master/FormEvents.php
-.. _`Form class`: https://github.com/symfony/symfony/blob/master/src/Symfony/Component/Form/Form.php
+One piece that may still be missing is the client-side updating of your form
+after the sport is selected. This should be handled by making an AJAX call
+back to your application. In that controller, you can bind your form, but
+instead of processing it, simply use the bound form to render the updated
+fields. The response from the AJAX call can then be used to update the view.
View
2  reference/dic_tags.rst
@@ -65,6 +65,8 @@ data_collector
For details on creating your own custom data collection, read the cookbook
article: :doc:`/cookbook/profiler/data_collector`.
+.. _dic-tags-form-type:
+
form.type
---------
Please sign in to comment.
Something went wrong with that request. Please try again.