Reputation: 16445
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
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