Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commitade0d4b

Browse files
committed
Merge branch 'dynamic_forms_after_PR8827' of github.com:Burgov/symfony-docs into Burgov-dynamic_forms_after_PR8827
Conflicts:cookbook/form/dynamic_form_modification.rst
2 parentsaa0a511 +862df0d commitade0d4b

File tree

1 file changed

+51
-156
lines changed

1 file changed

+51
-156
lines changed

‎cookbook/form/dynamic_form_modification.rst‎

Lines changed: 51 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -403,37 +403,36 @@ possible choices will depend on each sport. Football will have attack, defense,
403403
goalkeeper etc... Baseball will have a pitcher but will not have goalkeeper. You
404404
will need the correct options to be set in order for validation to pass.
405405

406-
The meetup is passed as an entityhiddenfield to the form. So we can access each
406+
The meetup is passed as an entity field to the form. So we can access each
407407
sport like this::
408408

409409
// src/Acme/DemoBundle/Form/Type/SportMeetupType.php
410410
namespace Acme\DemoBundle\Form\Type;
411-
411+
412412
use Symfony\Component\Form\FormBuilderInterface;
413413
use Symfony\Component\Form\FormEvent;
414414
use Symfony\Component\Form\FormEvents;
415-
415+
// ...
416+
416417
class SportMeetupType extends AbstractType
417418
{
418419
public function buildForm(FormBuilderInterface $builder, array $options)
419420
{
420421
$builder
421-
->add('number_of_people', 'text')
422-
->add('discount_coupon', 'text')
422+
->add('sport', 'entity', array(...))
423423
;
424-
$factory = $builder->getFormFactory();
425424

426425
$builder->addEventListener(
427426
FormEvents::PRE_SET_DATA,
428-
function(FormEvent $event)use ($factory){
427+
function(FormEvent $event) {
429428
$form = $event->getForm();
430429

431430
// this would be your entity, i.e. SportMeetup
432431
$data = $event->getData();
433432

434433
$positions = $data->getSport()->getAvailablePositions();
435434

436-
// ... proceed with customizing theform based on availablepositions
435+
$form->add('position', 'entity', array('choices' => $positions));
437436
}
438437
);
439438
}
@@ -454,173 +453,69 @@ On a form, we can usually listen to the following events:
454453
* ``BIND``
455454
* ``POST_BIND``
456455

457-
When listening to ``BIND`` and ``POST_BIND``, it's already "too late" to make
458-
changes to the form. Fortunately, ``PRE_BIND`` is perfect for this. There
459-
is, however, a big difference in what ``$event->getData()`` returns for each
460-
of these events. Specifically, in ``PRE_BIND``, ``$event->getData()`` returns
461-
the raw data submitted by the user.
456+
..versionadded::2.2.6
462457

463-
This can be used to get the ``SportMeetup`` id and retrieve it from the database,
464-
given you have a reference to the object manager (if using doctrine). In
465-
the end, you have an event subscriber that listens to two different events,
466-
requires some external services and customizes the form. In such a situation,
467-
it's probably better to define this as a service rather than using an anonymous
468-
function as the event listener callback.
469458

470-
The subscriber would now look like::
459+
The key is to add a ``POST_BIND`` listener to the field your new field is dependent
460+
on. If you add a POST_BIND listener to a form child, and add new children to the parent
461+
from there, the Form component will detect the new field automatically and maps it
462+
to the client data if it is available.
471463

472-
// src/Acme/DemoBundle/Form/EventListener/RegistrationSportListener.php
473-
namespace Acme\DemoBundle\Form\EventListener;
464+
The type would now look like::
474465

475-
use Symfony\Component\Form\FormFactoryInterface;
476-
use Doctrine\ORM\EntityManager;
477-
use Symfony\Component\Form\FormEvent;
478-
use Symfony\Component\Form\FormEvents;
479-
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
466+
// src/Acme/DemoBundle/Form/Type/SportMeetupType.php
467+
namespace Acme\DemoBundle\Form\Type;
480468

481-
class RegistrationSportListener implements EventSubscriberInterface
482-
{
483-
/**
484-
* @var FormFactoryInterface
485-
*/
486-
private $factory;
487-
488-
/**
489-
* @var EntityManager
490-
*/
491-
private $em;
492-
493-
/**
494-
* @param factory FormFactoryInterface
495-
*/
496-
public function __construct(FormFactoryInterface $factory, EntityManager $em)
497-
{
498-
$this->factory = $factory;
499-
$this->em = $em;
500-
}
469+
// ...
470+
Acme\DemoBundle\Entity\Sport;
471+
Symfony\Component\Form\FormInterface;
501472

502-
public static function getSubscribedEvents()
473+
class SportMeetupType extends AbstractType
474+
{
475+
public function buildForm(FormBuilderInterface $builder, array $options)
503476
{
504-
return array(
505-
FormEvents::PRE_BIND => 'preBind',
506-
FormEvents::PRE_SET_DATA => 'preSetData',
507-
);
508-
}
477+
$builder
478+
->add('sport', 'entity', array(...))
479+
;
509480

510-
/**
511-
* @param event FormEvent
512-
*/
513-
public function preSetData(FormEvent $event)
514-
{
515-
$meetup = $event->getData()->getMeetup();
481+
$formModifier = function(FormInterface $form, Sport $sport) {
482+
$positions = $data->getSport()->getAvailablePositions();
516483

517-
// Before binding the form, the "meetup" will be null
518-
if (null === $meetup) {
519-
return;
484+
$form->add('position', 'entity', array('choices' => $positions));
520485
}
521486

522-
$form = $event->getForm();
523-
$positions = $meetup->getSport()->getPositions();
487+
$builder->addEventListener(
488+
FormEvents::PRE_SET_DATA,
489+
function(FormEvent $event) {
490+
$form = $event->getForm();
524491

525-
$this->customizeForm($form, $positions);
526-
}
492+
//this would be your entity, i.e. SportMeetup
493+
$data = $event->getData();
527494

528-
public function preBind(FormEvent $event)
529-
{
530-
$data = $event->getData();
531-
$id = $data['event'];
532-
$meetup = $this->em
533-
->getRepository('AcmeDemoBundle:SportMeetup')
534-
->find($id);
535-
536-
if ($meetup === null) {
537-
$msg = 'The event %s could not be found for your registration';
538-
throw new \Exception(sprintf($msg, $id));
539-
}
540-
$form = $event->getForm();
541-
$positions = $meetup->getSport()->getPositions();
495+
$formModifier($event->getForm(), $sport);
496+
}
497+
);
542498

543-
$this->customizeForm($form, $positions);
544-
}
499+
$builder->get('meetup')->addEventListener(
500+
FormEvents::POST_BIND,
501+
function(FormEvent $event) use ($formModifier) {
502+
// It's important here to fetch $event->getForm()->getData(), as
503+
// $event->getData() will get you the client data (this is, the ID)
504+
$sport = $event->getForm()->getData();
545505

546-
protected function customizeForm($form, $positions)
547-
{
548-
// ... customize the form according to the positions
506+
$positions = $sport->getAvailablePositions();
507+
508+
// since we've added the listener to the child, we'll have to pass on
509+
// the parent to the callback functions!
510+
$formModifier($event->getForm()->getParent(), $sport);
511+
}
512+
);
549513
}
550514
}
551515

552516
You can see that you need to listen on these two events and have different callbacks
553-
only because in two different scenarios, the data that you can use is given in a
554-
different format. Other than that, this class always performs exactly the same
555-
things on a given form.
556-
557-
Now that you have that setup, register your form and the listener as services:
558-
559-
..configuration-block::
560-
561-
..code-block::yaml
562-
563-
# app/config/config.yml
564-
acme.form.sport_meetup:
565-
class:Acme\SportBundle\Form\Type\SportMeetupType
566-
arguments:[@acme.form.meetup_registration_listener]
567-
tags:
568-
-{ name: form.type, alias: acme_meetup_registration }
569-
acme.form.meetup_registration_listener
570-
class:Acme\SportBundle\Form\EventListener\RegistrationSportListener
571-
arguments:[@form.factory, @doctrine.orm.entity_manager]
572-
573-
..code-block::xml
574-
575-
<!-- app/config/config.xml-->
576-
<services>
577-
<serviceid="acme.form.sport_meetup"class="Acme\SportBundle\FormType\SportMeetupType">
578-
<argumenttype="service"id="acme.form.meetup_registration_listener" />
579-
<tagname="form.type"alias="acme_meetup_registration" />
580-
</service>
581-
<serviceid="acme.form.meetup_registration_listener"class="Acme\SportBundle\Form\EventListener\RegistrationSportListener">
582-
<argumenttype="service"id="form.factory" />
583-
<argumenttype="service"id="doctrine.orm.entity_manager" />
584-
</service>
585-
</services>
586-
587-
..code-block::php
588-
589-
// app/config/config.php
590-
$definition = new Definition('Acme\SportBundle\Form\Type\SportMeetupType');
591-
$definition->addTag('form.type', array('alias' => 'acme_meetup_registration'));
592-
$container->setDefinition(
593-
'acme.form.meetup_registration_listener',
594-
$definition,
595-
array('security.context')
596-
);
597-
$definition = new Definition('Acme\SportBundle\Form\EventListener\RegistrationSportListener');
598-
$container->setDefinition(
599-
'acme.form.meetup_registration_listener',
600-
$definition,
601-
array('form.factory', 'doctrine.orm.entity_manager')
602-
);
603-
604-
In this setup, the ``RegistrationSportListener`` will be a constructor argument
605-
to ``SportMeetupType``. You can then register it as an event subscriber on
606-
your form::
607-
608-
private $registrationSportListener;
609-
610-
public function __construct(RegistrationSportListener $registrationSportListener)
611-
{
612-
$this->registrationSportListener = $registrationSportListener;
613-
}
614-
615-
public function buildForm(FormBuilderInterface $builder, array $options)
616-
{
617-
// ...
618-
$builder->addEventSubscriber($this->registrationSportListener);
619-
}
620-
621-
And this should tie everything together. You can now retrieve your form from the
622-
controller, display it to a user, and validate it with the right choice options
623-
set for every possible kind of sport that our users are registering for.
517+
only because in two different scenarios, the data that you can use is available in different events.
518+
Other than that, the listeners always perform exactly the same things on a given form.
624519

625520
One piece that may still be missing is the client-side updating of your form
626521
after the sport is selected. This should be handled by making an AJAX call

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp