Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Symfony2: Change choices with ajax and validation

Tags:

forms

php

symfony

Scenario: I have a form with 2 selects. When user selects something from the first select, the second select gets populated with new values. This part works fine.

But the form does not get validated since it contains some choices that are not allowed in the initial form.

Form:

<?php

class MyType extends AbstractType
{
    private $category;

    public function __construct($category = null)
    {
        $this->category = $category;
    }

    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder->add('category', 'choice', array(
            'choices' => array(
                'foo' => 'foo',
                'bar' => 'bar'
            )
        );

        $builder->add('template', 'choice', array(
            'choices' => $this->loadChoices()
        );
    }

    private function loadChoices()
    {
        // load them from DB depending on the $this->category
    }
}

Initially the category is foo. So the templates for foo get loaded and set as choices. But if the user selects bar, the bar templates get loaded. But the form still has the foo choices and does not validate.

What is the best way to solve this?

One way I found was to just reinitiate the form in the controller:

<?php

$form = $this->createForm(new MyType());

if ($request->getMethod() === 'POST') {
    if ($request->request->has($form->getName())
        && isset($request->request->get($form->getName())['category'])) {
            $form = $this->createForm(new MyType($request->request->get($form->getName())['category']));
    }

    // ...
}

This works, but I cannot test it because it throws IllegalArgumentException when setting the value and just assumes default. Is there any better solution to this? Thanks in advance!

like image 665
smottt Avatar asked Nov 10 '12 11:11

smottt


1 Answers

I think u have to use Events to manage this, which is more correct way

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder->add('category', 'choice', array(
        'choices' => array(
            'foo' => 'foo',
            'bar' => 'bar'
        )
    ));

    $ff = $builder->getFormFactory();

    // function to add 'template' choice field dynamically 
    $func = function (FormEvent $e) use ($ff) {
        $data = $e->getData();
        $form = $e->getForm();
        if ($form->has('template')) {
            $form->remove('template');
        }

        $cat = isset($data['category'])?$data['category']:null;

        // here u can populate ur choices in a manner u do it in loadChoices
        $choices = array('1' => '1', '2' => '2');
        if ($cat == 'bar') {
            $choices = array('3' => '3', '4' => '4');
        }

        $form->add($ff->createNamed('template', 'choice', null, compact('choices')));
    };

    // Register the function above as EventListener on PreSet and PreBind
    $builder->addEventListener(FormEvents::PRE_SET_DATA, $func);
    $builder->addEventListener(FormEvents::PRE_BIND, $func);
}
like image 89
Ziumin Avatar answered Nov 06 '22 08:11

Ziumin