Search code examples
phpsymfony-formssymfony-2.1

How can I go about updating a ChoiceType's values in a FormEvent in Symfony 2.1?


I'm thinking I may need to extend LazyChoiceList and implement a new FormType, so far I have:

/**
 * A choice list for sorting choices.
 */
class SortChoiceList extends LazyChoiceList
{
    private $choices = array();

    public function getChoices() {
        return $this->choices;
    }

    public function setChoices(array $choices) {
        $this->choices = $choices;
        return $this;
    }

    protected function loadChoiceList() {
        return new SimpleChoiceList($this->choices);
    }
}

and

/**
 * @FormType
 */
class SortChoice extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->getParent()->addEventListener(FormEvents::PRE_SET_DATA, function($event) use ($options) {
            $options = (object) $options;

            $list = $options->choice_list;

            $data = $event->getData();

            if ($data->getLocation() && $data->getDistance()) {
                $list->setChoices(array(
                    '' => 'Distance',
                    'highest' => 'Highest rated',
                    'lowest' => 'Lowest rated'
                ));
            } else {
                $list->setChoices(array(
                    '' => 'Highest rated',
                    'lowest' => 'Lowest rated'
                ));
            }
        });
    }

    public function getParent()
    {
        return 'choice';
    }

    public function getName()
    {
        return 'sort_choice';
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'choice_list' => new SortChoiceList
        ));
    }
}

I've tried this sort of approach on all of the available FormEvent's, but I either don't have access to the data (null values) or updating the choice_list takes no effect, as far as I can see, because it has already been processed.


Solution

  • Turns out I didn't need to define a new type or LazyList at all and a better approach was to not add the field until I had the data, in my main form, like so:

    $builder->addEventListener(FormEvents::PRE_BIND, function($event) use ($builder) {
        $form = $event->getForm();
        $data = (object) array_merge(array('location' => null, 'distance' => null, 'sort_by' => null), $event->getData());
    
        if ($data->location && $data->distance) {
            $choices = array(
                '' => 'Distance',
                'highest' => 'Highest rated',
                'lowest' => 'Lowest rated'
            );
        } else {
            $choices = array(
                '' => 'Highest rated',
                'lowest' => 'Lowest rated'
            );
        }
    
        $form->add($builder->getFormFactory()->createNamed('sort_by', 'choice', $data->sort_by, array(
            'choices' => $choices,
            'required' => false
        )));
    });
    

    See: http://symfony.com/doc/master/cookbook/form/dynamic_form_generation.html