Dawid Ohia
Dawid Ohia

Reputation: 16445

Is it possible to have collection field in Symfony2 form with different choices?

I have a collection field with elements of type choice in my Symfony form. Each element should have different list o choices. How can I arrange this in Symfony2? I can't use choices option because every element will have the same choices. I have seen the choice_list option which takes an object that can produce the list of options, but I don't see how it could produce a different choices for different elements in collection.

Any idea how to deal with that?

Upvotes: 3

Views: 2381

Answers (1)

Antoine REYT
Antoine REYT

Reputation: 450

I think you need form event : http://symfony.com/doc/current/cookbook/form/dynamic_form_generation.html.

To change the default way the collection is made.

The main form is simple:

namespace Acme\Bundle\AcmeBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

use Acme\Bundle\AcmeBundle\Form\DescriptorDumpFieldsType;

class TranscodingType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('descriptorDumpFields', 'collection', array('type' => new DescriptorDumpFieldsType()));
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'data_class' => 'Acme\Bundle\AcmeBundle\Entity\Descriptor',
        );
    }

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

Just a simple form with a collection of sub forms.

The second one use a form subscriber who handle the form creation. (using form events)

So the first form is created normaly and add many DescriptorDumpFieldsType wich are dynamicly created.

namespace Acme\Bundle\AcmeBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormTypeInterface;

use Acme\Bundle\AcmeBundle\Form\EventListener\TranscodingSubscriber;

class DescriptorDumpFieldsType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $subscriber = new TranscodingSubscriber($builder->getFormFactory());
        $builder->addEventSubscriber($subscriber);
    }

    public function getDefaultOptions(array $options)
    {
        return array(
            'data_class' => 'Acme\Bundle\AcmeBundle\Entity\DescriptorDumpField',
        );
    }

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

The form subscriber :

namespace Acme\Bundle\AcmeBundle\Form\EventListener;

use Symfony\Component\Form\Event\DataEvent;
use Symfony\Component\Form\FormFactoryInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvents;

use Acme\Bundle\AcmeBundle\Entity\DumpField;

use Acme\Bundle\AcmeBundle\Form\Transcoding\DataTransformer\JsonToHumanDateTransformer;

class TranscodingSubscriber implements EventSubscriberInterface
{
    private $factory;

    public function __construct(FormFactoryInterface $factory)
    {
        $this->factory = $factory;
    }

    public static function getSubscribedEvents()
    {
        return array(FormEvents::SET_DATA => 'setData');
    }

    public function setData(DataEvent $event)
    {
        $data = $event->getData();
        $form = $event->getForm();

        if (!is_null($data)) {
            $this->buildForm($data, $form);            
        }
    }

    protected function buildForm($data, $form)
    {
        switch ($data->getDumpField()->getType()) {
            case DumpField::TYPE_ENUM:
                $type = 'enum'.ucfirst($data->getDumpField()->getKey());
                $class = 'dump_field_'.strtolower($data->getDumpField()->getKey());
                $form->add($this->factory->createNamed('collection', 'transcodings', null, array('required' => false, 'type' => $type, 'label' => $data->getDumpField()->getKey(), 'attr' => array('class' => $class))));
                break;
            case DumpField::TYPE_DATE:
                $transformer = new JsonToHumanDateTransformer();
                $class = 'dump_field_'.strtolower($data->getDumpField()->getKey());
                $builder = $this->factory->createNamedBuilder('human_date', 'params', null, array('label' => $data->getDumpField()->getKey(), 'attr' => array('class' => $class)));
                $builder->prependNormTransformer($transformer);
                $form->add($builder->getForm());
                break;
        }
    }
}

So you can customize the way you want, each sub-form of the collection in buildForm.

Upvotes: 3

Related Questions