Adrian
Adrian

Reputation: 1370

Adding choices dynamically using EventSubscriber to Symfony2 form

I am trying to set the choices for a form select dynamically since the choices come from a service call. However, when the form renders in the view, the choices are not there.

I'm doing the following in the FormType

<?php

namespace My\Form\Customer;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;

class ItemReturnRequestForm extends AbstractType
{

    /**
     * @var EventSubscriberInterface
     */
    protected $reasonsSubscriber;

    /**
     * Returns the name of this type.
     *
     * @return string The name of this type
     */
    public function getName()
    {
        return 'item_return_request';
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('reason', 'choice', [
            'label' => 'order.returns.reason_for_return',
            'required' => true,
            'multiple' => false,
            'expanded' => false,
            'placeholder' => 'order.returns.reasons.empty',
            'empty_data' => null,
        ]);

        $builder->addEventSubscriber($this->reasonsSubscriber);
    }

    /**
     * @param EventSubscriberInterface $reasonsSubscriber
     */
    public function setReasonsSubscriber(EventSubscriberInterface $reasonsSubscriber)
    {
        $this->reasonsSubscriber = $reasonsSubscriber;
    }
}

The FormType has a service definition which injects the EventSubscriber instance since that is also a service definition with it's own dependencies.

The EventSubscrbier looks like

<?php

namespace My\Form\EventSubscriber;

use My\Customer\ItemReturnAware;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class ReturnReasonEventSubscriber implements EventSubscriberInterface
{
    use ItemReturnAware;

    public static function getSubscribedEvents()
    {
        return [
            FormEvents::PRE_SET_DATA => 'getReturnReasons',
        ];
    }

    public function getReturnReasons(FormEvent $event)
    {
        $form = $event->getForm();

        if ($form->has('reason')) {
            $options = $form->get('reason')->getConfig()->getOptions();
            $options['choices'] = $this->itemReturnService->getReasons();

            $form->add('reason', 'choice', $options);
        }
    }
}

Everything seems to work fine up until this point. Using XDEBUG I can see that the EventSubscriber is being triggered. The service call sets $option['choices'] to the expected array value & the field is added successfully.

However, when the form gets rendered. it's as if the EventSubscriber had never been called.

If it makes a difference, the options array is an un-ordered numeric list.

i.e.

$options = [
    10 => 'First choice',
    15 => 'Second choice',
    20 => 'Third choice',
];

Any ideas?

Upvotes: 4

Views: 3670

Answers (1)

Nicolas
Nicolas

Reputation: 601

This is an ancient question, but today I found it on the top results searching for event listener to modify form choices.

In my context I have an entity programmatically created, and I redirect the user to the editAction to finish filling the fields. I have one choice that I can apply only in this particular case, I don't want to allow my user to use it outside it.

That's why I user the POST_SET_DATA event, because I already have an entity with populated fields.

This event listener is set in the formType, inside the

public function buildForm(FormBuilderInterface $builder, array $options)
{

Here a working solution for symfony 3.4:

$builder->addEventListener(FormEvents::POST_SET_DATA, function (FormEvent $event) {
// get the form from the event
$form = $event->getForm();

if ('myParticularMode' == $form->get('mode')->getData()) {
    // get the field options
    $options = $form->get('mode')->getConfig()->getOptions();
    // add the mode to the choices array
    $options['choices']['MY_PARTICULAR_MODE'] = 'myParticularMode_display_name';
    $form->add('mode', ChoiceType::class, $options);
}

});

If you want to replace the choices, you can remove this:

$options = $form->get('mode')->getConfig()->getOptions();

and set a new array for choices.

Upvotes: 2

Related Questions