Symfony 2 Embedded Form Collection Many to Many

匿名 (未验证) 提交于 2019-12-03 02:45:02

问题:

I have 2 Entities - User and Group. They have a many-to-many relationship and Group is used to store a users' roles.

I'm trying to make a User edit form by adding a collection, I want to be able to add a new role by selecting it from a dropdown (limited to what's already in the DB)

UserType.php:

class UserType extends AbstractType {     public function buildForm(FormBuilderInterface $builder, array $options)     {         $builder             ->add('username')             ->add('email')             ->add('forename')             ->add('surname')             ->add('isActive')             ->add('joinDate', 'date', array('input' => 'datetime', 'format' => 'dd-MM-yyyy'))             ->add('lastActive', 'date', array('input' => 'datetime', 'format' => 'dd-MM-yyyy'))             ->add('groups', 'collection', array(                     'type' => new GroupType(),                     'allow_add' => true,                     ))         ;     }      public function setDefaultOptions(OptionsResolverInterface $resolver)     {         $resolver->setDefaults(array(             'data_class' => 'Sfox\CoreBundle\Entity\User'         ));     } } 

and GroupType.php:

class GroupType extends AbstractType {     public function buildForm(FormBuilderInterface $builder, array $options)     {         $builder             ->add('name')             ->add('role');     }      public function setDefaultOptions(OptionsResolverInterface $resolver)     {         $resolver->setDefaults(array(                 "data_class" => 'Sfox\CoreBundle\Entity\Group'                 ));     } } 

This displays the roles in the form in basic text boxes, but if I add an entry to the form, it will cascade persist a new entry into Groups and if I were to edit an entry, it would change the underlying Group data.

I tried making a GroupSelectType.php:

class GroupSelectType extends AbstractType {     public function buildForm(FormBuilderInterface $builder, array $options)     {         $builder             ->add('role', 'entity', array('class'=>'SfoxCoreBundle:Group', 'property'=>'name'));     }      public function setDefaultOptions(OptionsResolverInterface $resolver)     {         $resolver->setDefaults(array(                 "data_class" => 'Sfox\CoreBundle\Entity\Group'                 ));     } } 

Adding the field as an "entity" type, this displays the correct select box (but with the default values) I cant seem to bind it to the UserType form!

All I want the form to do is modify the underlying 'groups' ArrayCollection in the User entity.

Does anyone know how I can achieve this?

回答1:

Well I worked out a solution for anyone else struggling with similar problems...

I had to create a custom form type and declare it as a service so I could pass in the Entity Manager. I then needed to make a dataTransformer to change my group objects into an integer for the form

Custom GroupSelectType:

class GroupSelectType extends AbstractType  {     /**      * @var ObjectManager      */     private $om;      private $choices;      /**      * @param ObjectManager $om      */     public function __construct(ObjectManager $om)     {         $this->om = $om;          // Build our choices array from the database         $groups = $om->getRepository('SfoxCoreBundle:Group')->findAll();         foreach ($groups as $group)         {             // choices[key] = label             $this->choices[$group->getId()] = $group->getName() . " [". $group->getRole() ."]";         }     }      public function buildForm(FormBuilderInterface $builder, array $options)     {         $transformer = new GroupToNumberTransformer($this->om);         $builder->addModelTransformer($transformer);     }      public function setDefaultOptions(OptionsResolverInterface $resolver)     {         $resolver->setDefaults(array(                 "choices" => $this->choices,                 ));     }      public function getParent()     {         return 'choice';     }      public function getName()     {         return 'group_select';     } } 

In the constructor I'm getting all available groups and putting them into a "choices" array which is passed to the select box as an option.

You'll also notice I'm using a custom data transformer, this is to change the groupId (which is used in the rendering of the form) to a Group entity. I made the GroupSelectType a service as well and passed in the [@doctrine.orm.entity_manager]

services.yml (bundle config):

services:     sfox_core.type.group_select:         class: Sfox\CoreBundle\Form\Type\GroupSelectType         arguments: [@doctrine.orm.entity_manager]         tags:           - { name: form.type, alias: group_select } 

GroupToNumberTranformer.php

class GroupToNumberTransformer implements DataTransformerInterface {     /**      * @var ObjectManager      */     private $om;      /**      * @param ObjectManager $om      */     public function __construct(ObjectManager $om)     {         $this->om = $om;     }      /**      * Transforms an object (group) to a string (number).      *      * @param  Group|null $group      * @return string      */     public function transform($group)     {         if (null === $group) {             return "";         }          return $group->getId();     }      /**      * Transforms a string (number) to an object (group).      *      * @param  string $number      * @return Group|null      * @throws TransformationFailedException if object (group) is not found.      */     public function reverseTransform($number)     {         if (!$number) {             return null;         }          $group = $this->om         ->getRepository('SfoxCoreBundle:Group')         ->findOneBy(array('id' => $number))         ;          if (null === $group) {             throw new TransformationFailedException(sprintf(                     'Group with ID "%s" does not exist!',                     $number             ));         }          return $group;     } } 

And my modified UserType.php - Notice I'm using my custom form type "group_select" now as it's running as a service:

class UserType extends AbstractType {     private $entityManager;      public function __construct($entityManager)     {         $this->entityManager = $entityManager;     }      public function buildForm(FormBuilderInterface $builder, array $options)     {         $transformer = new GroupToNumberTransformer($this->entityManager);          $builder             ->add('username')             ->add('email')             ->add('forename')             ->add('surname')             ->add('isActive')             ->add('joinDate', 'date', array('input' => 'datetime', 'format' => 'dd-MM-yyyy'))             ->add('lastActive', 'date', array('input' => 'datetime', 'format' => 'dd-MM-yyyy'));         $builder             ->add(                 $builder->create('groups', 'collection', array(                     'type' => 'group_select',                     'allow_add' => true,                     'options' => array(                             'multiple' => false,                             'expanded' => false,                             )                     ))         );     }      public function setDefaultOptions(OptionsResolverInterface $resolver)     {         $resolver->setDefaults(array(             'data_class' => 'Sfox\CoreBundle\Entity\User'         ));     }      public function getName()     {         return 'sfox_corebundle_usertype';     } } 


标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!