From 4ec843febc2a99e98871e8f6536e80b98c2e229f Mon Sep 17 00:00:00 2001 From: Pavel Volokitin Date: Sun, 31 Mar 2013 00:16:21 +0600 Subject: [PATCH 1/8] Add note about buffering in StreamedResponse. --- components/http_foundation/introduction.rst | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index a75777f7798..4b088494788 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -387,6 +387,18 @@ represented by a PHP callable instead of a string:: }); $response->send(); +.. note:: + + The ``flush()`` function does not flush bufferring. So if + ``ob_start()`` has been called before or php.ini option + ``output_buffering`` is not disabled (which is on some + installations by default), you have to call ``ob_flush()`` before + ``flush()``. + + But not only php can buffer output. Your web-server can also do + it. Even more, if you use fastcgi, buffering can't be disabled at + all. + Downloading Files ~~~~~~~~~~~~~~~~~ From d5b318d923960f8c186a02bc7b82f31cde4e0e00 Mon Sep 17 00:00:00 2001 From: Sebastian Goettschkes Date: Sat, 30 Mar 2013 21:33:02 +0100 Subject: [PATCH 2/8] Documenting the non-blocking features of the Process component --- components/process.rst | 42 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/components/process.rst b/components/process.rst index 973b572e64d..a840641603c 100644 --- a/components/process.rst +++ b/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. When executing a long running command (like rsync-ing files to a remote server), you can give feedback to the end user in real-time by passing an @@ -51,6 +52,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:: From 0826e3389949dd97c7cd813725a94819f572d5d7 Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Sat, 30 Mar 2013 10:23:05 -0500 Subject: [PATCH 3/8] [#1842] Heavily reworking the dynamic for modification chapter - there is still quite a bit left to do --- cookbook/form/create_custom_field_type.rst | 2 + cookbook/form/dynamic_form_modification.rst | 398 ++++++++++++-------- reference/dic_tags.rst | 2 + 3 files changed, 245 insertions(+), 157 deletions(-) diff --git a/cookbook/form/create_custom_field_type.rst b/cookbook/form/create_custom_field_type.rst index 709d9e3a6f9..a12fbc3c875 100644 --- a/cookbook/form/create_custom_field_type.rst +++ b/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 ------------------------------------- diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index d80733f2afa..7f27cb4d871 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/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 ` 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,10 +102,7 @@ to an Event Subscriber:: $builder->add('price'); } - public function getName() - { - return 'product'; - } + // ... } The event subscriber is passed the FormFactory object in its constructor so @@ -83,7 +112,7 @@ notified of the dispatched event during form creation. .. _`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 @@ -118,62 +147,44 @@ 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($this->factory->createNamed('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; @@ -181,7 +192,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 { @@ -206,13 +216,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; @@ -223,19 +231,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; @@ -251,66 +266,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 - + @@ -319,7 +366,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', @@ -327,39 +374,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'); + + // ... + } + } + +You can also easily embed the form type into another form:: - return compact('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) @@ -374,7 +426,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 } @@ -382,35 +438,42 @@ sport like this:: } } +When you're building this form to display to the user for the first time, +then this example works perfectly. + +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. -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. +On a form, we can usually listen to the following events: -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)``. +* ``PRE_SET_DATA`` +* ``POST_SET_DATA`` +* ``PRE_BIND`` +* ``BIND`` +* ``POST_BIND`` -On a form, we can usually listen to the following events:: +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. - * ``PRE_SET_DATA`` - * ``POST_SET_DATA`` - * ``PRE_BIND`` - * ``BIND`` - * ``POST_BIND`` +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. -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. +The subscriber would now look like:: -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. + // src/Acme/DemoBundle/Form/EventListener/RegistrationSportListener.php + namespace Acme\DemoBundle\Form\EventListener; -Our subscriber would now look like:: + use Symfony\Component\Form\FormFactoryInterface; + use Doctrine\ORM\EntityManager; + use Symfony\Component\Form\FormEvent; class RegistrationSportListener implements EventSubscriberInterface { @@ -420,14 +483,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; @@ -435,16 +498,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(); @@ -454,19 +517,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)); } @@ -482,12 +546,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:: @@ -495,23 +559,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 - + - + @@ -520,24 +584,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. diff --git a/reference/dic_tags.rst b/reference/dic_tags.rst index 46bce20daff..1d9cd0c661f 100644 --- a/reference/dic_tags.rst +++ b/reference/dic_tags.rst @@ -63,6 +63,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 --------- From cc1c01c3ac2aaf36a67c59ce44318e34bc45974c Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 3 Apr 2013 21:16:27 -0500 Subject: [PATCH 4/8] [#2417] Fixing line breaks thanks to @WouterJ --- cookbook/form/dynamic_form_modification.rst | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cookbook/form/dynamic_form_modification.rst b/cookbook/form/dynamic_form_modification.rst index 7f27cb4d871..0e8d093e2b4 100644 --- a/cookbook/form/dynamic_form_modification.rst +++ b/cookbook/form/dynamic_form_modification.rst @@ -159,8 +159,9 @@ might look like the following:: .. tip:: 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. + ``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:: From 1f82d0ae81509f6b602160e6f1beedeef5faf6ff Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 3 Apr 2013 21:27:32 -0500 Subject: [PATCH 5/8] [#2422] Proofreading notes on output buffering and StreamedResponse --- components/http_foundation/introduction.rst | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/components/http_foundation/introduction.rst b/components/http_foundation/introduction.rst index 4b088494788..4206ce78fa5 100644 --- a/components/http_foundation/introduction.rst +++ b/components/http_foundation/introduction.rst @@ -389,15 +389,13 @@ represented by a PHP callable instead of a string:: .. note:: - The ``flush()`` function does not flush bufferring. So if - ``ob_start()`` has been called before or php.ini option - ``output_buffering`` is not disabled (which is on some - installations by default), you have to call ``ob_flush()`` before - ``flush()``. - - But not only php can buffer output. Your web-server can also do - it. Even more, if you use fastcgi, buffering can't be disabled at - all. + 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. Downloading Files ~~~~~~~~~~~~~~~~~ From 3ca138658ac0d656586a31553c1a7d053bf54e0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micka=C3=ABl=20Andrieu?= Date: Sat, 30 Mar 2013 19:44:18 +0100 Subject: [PATCH 6/8] Facelift of book/controller.rst issue #2377 --- book/templating.rst | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/book/templating.rst b/book/templating.rst index 1aadb42270d..543b86f680e 100644 --- a/book/templating.rst +++ b/book/templating.rst @@ -1300,8 +1300,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 From 66ed966134832f3f65d99ed76b8661823d700ffc Mon Sep 17 00:00:00 2001 From: Eric Poe Date: Sat, 30 Mar 2013 14:13:39 -0500 Subject: [PATCH 7/8] Update installation.rst Added some symlink support information. --- book/installation.rst | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/book/installation.rst b/book/installation.rst index becb8561c74..d6fbec78c48 100644 --- a/book/installation.rst +++ b/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 From c1c44245685cb6c6417b31a5473c129de083035e Mon Sep 17 00:00:00 2001 From: Ryan Weaver Date: Wed, 3 Apr 2013 21:52:38 -0500 Subject: [PATCH 8/8] Fixing language --- components/process.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/process.rst b/components/process.rst index a840641603c..9d657cd2981 100644 --- a/components/process.rst +++ b/components/process.rst @@ -100,7 +100,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::