首页 文章

Symfony2实体集合 - 如何添加/删除与现有实体的关联?

提问于
浏览
67

1.快速浏览

1.1目标

我想要实现的是创建/编辑用户工具 . 可编辑的字段是:

  • 用户名(类型:文字)

  • plainPassword(类型:密码)

  • 电子邮件(类型:电子邮件)

  • groups(类型:集合)

  • avoRoles(类型:集合)

注意:最后一个属性未命名为 $roles 因为我的User类正在扩展FOSUserBundle的User类并且覆盖角色带来了更多问题 . 为了避免它们,我只是决定在 $avoRoles 下存储我的角色集合 .

1.2用户界面

My template由两部分组成:

  • 用户表单

  • 表显示$ userRepository-> findAllRolesExceptOwnedByUser($ user);

注意:findAllRolesExceptOwnedByUser()是一个自定义存储库函数,返回所有角色的子集(尚未分配给$ user的角色) .

1.3所需功能

1.3.1添加角色:

WHEN user clicks "+" (add) button in Roles table  
    THEN jquery removes that row from Roles table  
    AND  jquery adds new list item to User form (avoRoles list)

1.3.2删除角色:

WHEN user clicks "x" (remove) button in  User form (avoRoles list)  
    THEN jquery removes that list item from User form (avoRoles list)  
    AND  jquery adds new row to Roles table

1.3.3保存更改:

WHEN user clicks "Zapisz" (save) button  
    THEN user form submits all fields (username, password, email, avoRoles, groups)  
    AND  saves avoRoles as an ArrayCollection of Role entities (ManyToMany relation)  
    AND  saves groups as an ArrayCollection of Role entities (ManyToMany relation)

注意:只能将现有角色和组分配给用户 . 如果由于任何原因找不到,表格不应该验证 .


2.代码

在本节中,我将介绍/或简要介绍此操作背后的代码 . 如果描述不够,你需要看到代码告诉我,我会粘贴它 . 我不是首先将它粘贴在一起,以避免使用不必要的代码向您发送垃圾邮件 .

2.1用户类

我的User类扩展了FOSUserBundle用户类 .

namespace Avocode\UserBundle\Entity;

use FOS\UserBundle\Entity\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
use Avocode\CommonBundle\Collections\ArrayCollection;
use Symfony\Component\Validator\ExecutionContext;

/**
 * @ORM\Entity(repositoryClass="Avocode\UserBundle\Repository\UserRepository")
 * @ORM\Table(name="avo_user")
 */
class User extends BaseUser
{
    const ROLE_DEFAULT = 'ROLE_USER';
    const ROLE_SUPER_ADMIN = 'ROLE_SUPER_ADMIN';

    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\generatedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\ManyToMany(targetEntity="Group")
     * @ORM\JoinTable(name="avo_user_avo_group",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
     * )
     */
    protected $groups;

    /**
     * @ORM\ManyToMany(targetEntity="Role")
     * @ORM\JoinTable(name="avo_user_avo_role",
     *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
     *      inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
     * )
     */
    protected $avoRoles;

    /**
     * @ORM\Column(type="datetime", name="created_at")
     */
    protected $createdAt;

    /**
     * User class constructor
     */
    public function __construct()
    {
        parent::__construct();

        $this->groups = new ArrayCollection();        
        $this->avoRoles = new ArrayCollection();
        $this->createdAt = new \DateTime();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set user roles
     * 
     * @return User
     */
    public function setAvoRoles($avoRoles)
    {
        $this->getAvoRoles()->clear();

        foreach($avoRoles as $role) {
            $this->addAvoRole($role);
        }

        return $this;
    }

    /**
     * Add avoRole
     *
     * @param Role $avoRole
     * @return User
     */
    public function addAvoRole(Role $avoRole)
    {
        if(!$this->getAvoRoles()->contains($avoRole)) {
          $this->getAvoRoles()->add($avoRole);
        }

        return $this;
    }

    /**
     * Get avoRoles
     *
     * @return ArrayCollection
     */
    public function getAvoRoles()
    {
        return $this->avoRoles;
    }

    /**
     * Set user groups
     * 
     * @return User
     */
    public function setGroups($groups)
    {
        $this->getGroups()->clear();

        foreach($groups as $group) {
            $this->addGroup($group);
        }

        return $this;
    }

    /**
     * Get groups granted to the user.
     *
     * @return Collection
     */
    public function getGroups()
    {
        return $this->groups ?: $this->groups = new ArrayCollection();
    }

    /**
     * Get user creation date
     *
     * @return DateTime
     */
    public function getCreatedAt()
    {
        return $this->createdAt;
    }
}

2.2角色类

我的Role类扩展了Symfony Security Component Core Role类 .

namespace Avocode\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Avocode\CommonBundle\Collections\ArrayCollection;
use Symfony\Component\Security\Core\Role\Role as BaseRole;

/**
 * @ORM\Entity(repositoryClass="Avocode\UserBundle\Repository\RoleRepository")
 * @ORM\Table(name="avo_role")
 */
class Role extends BaseRole
{    
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\generatedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", unique="TRUE", length=255)
     */
    protected $name;

    /**
     * @ORM\Column(type="string", length=255)
     */
    protected $module;

    /**
     * @ORM\Column(type="text")
     */
    protected $description;

    /**
     * Role class constructor
     */
    public function __construct()
    {
    }

    /**
     * Returns role name.
     * 
     * @return string
     */    
    public function __toString()
    {
        return (string) $this->getName();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set name
     *
     * @param string $name
     * @return Role
     */
    public function setName($name)
    {      
        $name = strtoupper($name);
        $this->name = $name;

        return $this;
    }

    /**
     * Get name
     *
     * @return string 
     */
    public function getName()
    {
        return $this->name;
    }

    /**
     * Set module
     *
     * @param string $module
     * @return Role
     */
    public function setModule($module)
    {
        $this->module = $module;

        return $this;
    }

    /**
     * Get module
     *
     * @return string 
     */
    public function getModule()
    {
        return $this->module;
    }

    /**
     * Set description
     *
     * @param text $description
     * @return Role
     */
    public function setDescription($description)
    {
        $this->description = $description;

        return $this;
    }

    /**
     * Get description
     *
     * @return text 
     */
    public function getDescription()
    {
        return $this->description;
    }
}

2.3群组类

由于我和团队一起遇到与角色相同的问题,我在这里跳过它们 . 如果我开始工作,我知道我可以对团队做同样的事情 .

2.4控制器

namespace Avocode\UserBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Security\Core\SecurityContext;
use JMS\SecurityExtraBundle\Annotation\Secure;
use Avocode\UserBundle\Entity\User;
use Avocode\UserBundle\Form\Type\UserType;

class UserManagementController extends Controller
{
    /**
     * User create
     * @Secure(roles="ROLE_USER_ADMIN")
     */
    public function createAction(Request $request)
    {      
        $em = $this->getDoctrine()->getEntityManager();

        $user = new User();
        $form = $this->createForm(new UserType(array('password' => true)), $user);

        $roles = $em->getRepository('AvocodeUserBundle:User')
                    ->findAllRolesExceptOwned($user);
        $groups = $em->getRepository('AvocodeUserBundle:User')
                    ->findAllGroupsExceptOwned($user);

        if($request->getMethod() == 'POST' && $request->request->has('save')) {
            $form->bindRequest($request);

            if($form->isValid()) {
                /* Persist, flush and redirect */
                $em->persist($user);
                $em->flush();
                $this->setFlash('avocode_user_success', 'user.flash.user_created');
                $url = $this->container->get('router')->generate('avocode_user_show', array('id' => $user->getId()));

                return new RedirectResponse($url);
            }
        }

        return $this->render('AvocodeUserBundle:UserManagement:create.html.twig', array(
          'form' => $form->createView(),
          'user' => $user,
          'roles' => $roles,
          'groups' => $groups,
        ));
    }
}

2.5自定义存储库

发布此信息不是必要的,因为它们工作得很好 - 它们返回所有角色/组的子集(未分配给用户的组) .

2.6 UserType

用户类型:

namespace Avocode\UserBundle\Form\Type;

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

class UserType extends AbstractType
{    
    private $options; 

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

    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder->add('username', 'text');

        // password field should be rendered only for CREATE action
        // the same form type will be used for EDIT action
        // thats why its optional

        if($this->options['password'])
        {
          $builder->add('plainpassword', 'repeated', array(
                        'type' => 'text',
                        'options' => array(
                          'attr' => array(
                            'autocomplete' => 'off'
                          ),
                        ),
                        'first_name' => 'input',
                        'second_name' => 'confirm', 
                        'invalid_message' => 'repeated.invalid.password',
                     ));
        }

        $builder->add('email', 'email', array(
                        'trim' => true,
                     ))

        // collection_list is a custom field type
        // extending collection field type
        //
        // the only change is diffrent form name
        // (and a custom collection_list_widget)
        // 
        // in short: it's a collection field with custom form_theme
        // 
                ->add('groups', 'collection_list', array(
                        'type' => new GroupNameType(),
                        'allow_add' => true,
                        'allow_delete' => true,
                        'by_reference' => true,
                        'error_bubbling' => false,
                        'prototype' => true,
                     ))
                ->add('avoRoles', 'collection_list', array(
                        'type' => new RoleNameType(),
                        'allow_add' => true,
                        'allow_delete' => true,
                        'by_reference' => true,
                        'error_bubbling' => false,
                        'prototype' => true,
                     ));
    }

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

    public function getDefaultOptions(array $options){

        $options = array(
          'data_class' => 'Avocode\UserBundle\Entity\User',
        );

        // adding password validation if password field was rendered

        if($this->options['password'])
          $options['validation_groups'][] = 'password';

        return $options;
    }
}

2.7 RoleNameType

这个表单应该呈现:

  • 隐藏角色ID

  • 角色名称(仅限阅读)

  • 隐藏模块(只读)

  • 隐藏说明(仅限阅读)

  • 删除(x)按钮

模块和描述呈现为隐藏字段,因为当Admin从用户删除角色时,该角色应该由jQuery添加到角色表 - 此表包含模块和描述列 .

namespace Avocode\UserBundle\Form\Type;

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

class RoleNameType extends AbstractType
{
    public function buildForm(FormBuilder $builder, array $options)
    {
        $builder            
            ->add('', 'button', array(
              'required' => false,
            ))  // custom field type rendering the "x" button

            ->add('id', 'hidden')

            ->add('name', 'label', array(
              'required' => false,
            )) // custom field type rendering <span> item instead of <input> item

            ->add('module', 'hidden', array('read_only' => true))
            ->add('description', 'hidden', array('read_only' => true))
        ;        
    }

    public function getName()
    {
        // no_label is a custom widget that renders field_row without the label

        return 'no_label';
    }

    public function getDefaultOptions(array $options){
        return array('data_class' => 'Avocode\UserBundle\Entity\Role');
    }
}

3.当前/已知问题

3.1案例1:如上所述的配置

以上配置返回错误:

Property "id" is not public in class "Avocode\UserBundle\Entity\Role". Maybe you should create the method "setId()"?

但不应要求使用ID的setter .

  • 首先因为我不想创建一个新角色 . 我只想在现有角色和用户实体之间创建关系 .

  • 即使我确实想要创建一个新角色,它的ID应该是自动生成的:

/ **

  • @ORM \ Id

  • @ORM \ Column(type = "integer")

  • @ORM \ generatedValue(strategy = "AUTO")* / protected $ id;

3.2案例2:在Role实体中为ID属性添加了setter

我认为这是错的,但我这样做是为了确定 . 将此代码添加到Role实体后:

public function setId($id)
{
    $this->id = $id;
    return $this;
}

如果我创建新用户并添加角色,那么SAVE ......会发生什么:

  • 已创建新用户

  • 新用户具有分配了所需ID的角色(是的!)

  • but that role's name is overwritten with empty string (真可惜!)

显然,这不是我想要的 . 我不想编辑/覆盖角色 . 我只想在它们和用户之间添加关系 .

3.3案例3:Jeppe建议的解决方法

当我第一次遇到这个问题时,我最终得到了一个解决方法,就像Jeppe建议的那样 . 今天(出于其他原因)我不得不重新制作表单/视图,并且解决方法已停止工作 .

Case3 UserManagementController中的更改 - > createAction:

// in createAction
  // instead of $user = new User
  $user = $this->updateUser($request, new User());

  //and below updateUser function


    /**
     * Creates mew iser and sets its properties
     * based on request
     * 
     * @return User Returns configured user
     */
    protected function updateUser($request, $user)
    {
        if($request->getMethod() == 'POST')
        {
          $avo_user = $request->request->get('avo_user');

          /**
           * Setting and adding/removeing groups for user
           */
          $owned_groups = (array_key_exists('groups', $avo_user)) ? $avo_user['groups'] : array();
          foreach($owned_groups as $key => $group) {
            $owned_groups[$key] = $group['id'];
          }

          if(count($owned_groups) > 0)
          {
            $em = $this->getDoctrine()->getEntityManager();
            $groups = $em->getRepository('AvocodeUserBundle:Group')->findById($owned_groups);
            $user->setGroups($groups);
          }

          /**
           * Setting and adding/removeing roles for user
           */
          $owned_roles = (array_key_exists('avoRoles', $avo_user)) ? $avo_user['avoRoles'] : array();
          foreach($owned_roles as $key => $role) {
            $owned_roles[$key] = $role['id'];
          }

          if(count($owned_roles) > 0)
          {
            $em = $this->getDoctrine()->getEntityManager();
            $roles = $em->getRepository('AvocodeUserBundle:Role')->findById($owned_roles);
            $user->setAvoRoles($roles);
          }

          /**
           * Setting other properties
           */
          $user->setUsername($avo_user['username']);
          $user->setEmail($avo_user['email']);

          if($request->request->has('generate_password'))
            $user->setPlainPassword($user->generateRandomPassword());  
        }

        return $user;
    }

不幸的是,这不会改变任何东西..结果是CASE1(没有ID设置器)或CASE2(带ID设置器) .

3.4案例4:由userfriendly建议

将cascade = {“persist”,“remove”}添加到映射中 .

/**
 * @ORM\ManyToMany(targetEntity="Group", cascade={"persist", "remove"})
 * @ORM\JoinTable(name="avo_user_avo_group",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="group_id", referencedColumnName="id")}
 * )
 */
protected $groups;

/**
 * @ORM\ManyToMany(targetEntity="Role", cascade={"persist", "remove"})
 * @ORM\JoinTable(name="avo_user_avo_role",
 *      joinColumns={@ORM\JoinColumn(name="user_id", referencedColumnName="id")},
 *      inverseJoinColumns={@ORM\JoinColumn(name="role_id", referencedColumnName="id")}
 * )
 */
protected $avoRoles;

并在FormType中将 by_reference 更改为 false

// ...

                ->add('avoRoles', 'collection_list', array(
                        'type' => new RoleNameType(),
                        'allow_add' => true,
                        'allow_delete' => true,
                        'by_reference' => false,
                        'error_bubbling' => false,
                        'prototype' => true,
                     ));

// ...

保持3.3中建议的变通方法代码确实改变了一些东西:

  • 用户和角色之间的关联是 not created

  • ..但是角色实体的名称被空字符串覆盖(如3.2中所示)

所以..它确实改变了一些东西,但方向错误 .

4.版本

4.1 Symfony2 v2.0.15

4.2 Doctrine2 v2.1.7

4.3 FOSUserBundle版本:6fb81861d84d460f1d070ceb8ec180aac841f7fa

5.摘要

我尝试了很多不同的方法(以上只是最近的方法),花了几个小时研究代码,google'ing并寻找答案,我无法让这个工作 .

任何帮助将不胜感激 . 如果您需要了解任何内容,我会发布您需要的任何代码部分 .

5 回答

  • 0

    所以一年过去了,这个问题已经成为现实很受欢迎 . Symfony已经改变,我的技能和知识也有所改善,我目前处理这个问题的方法也是如此 .

    我为symfony2创建了一组表单扩展(请参阅github上的FormExtensionsBundle项目),它们包含用于处理One / Many ToMany 关系的表单类型 .

    在编写这些内容时,向控制器添加自定义代码来处理集合是不可接受的 - 表单扩展应该易于使用,开箱即用,让开发人员的生活更轻松,而不是更难 . 还..记得..干!

    所以我不得不在其他地方移动添加/删除关联代码 - 而正确的地方自然是一个EventListener :)

    看看EventListener/CollectionUploadListener.php文件,看看我们现在如何处理这个问题 .

    PS . 在这里复制代码是不必要的,最重要的是像这样的东西应该在EventListener中实际处理 .

  • 13

    你需要更多的实体:
    USER
    id_user(类型:整数)
    用户名(类型:文字)
    plainPassword(类型:密码)
    电邮(类型:电邮)


    GROUPS
    id_group(类型:整数)
    descripcion(类型:文本)


    AVOROLES
    id_avorole(类型:整数)
    descripcion(类型:文本)


    • USER_GROUP*
      id_user_group(类型:整数)
      id_user(类型:整数)(这是用户实体的id)
      id_group(类型:整数)(这是组实体上的id)

    • USER_AVOROLES*
      id_user_avorole(类型:整数)
      id_user(类型:整数)(这是用户实体的id)
      id_avorole(类型:整数)(这是avorole实体上的id)

    你可以举例如下:
    用户:
    id:3
    用户名:john
    plainPassword:johnpw
    电子邮件:john@email.com

    组:
    id_group:5
    描述:第5组

    USER_GROUP:
    id_user_group:1
    id_user:3
    id_group:5
    此用户可以有多个组,所以在另一行

  • 10

    1.变通方法解决方案

    Jeppe Marianger-Lam建议的解决方案解决方案目前是我所知道的唯一一个解决方案 .

    1.1为什么它在我的情况下停止工作?

    我将RoleNameType(由于其他原因)更改为:

    • ID(隐藏)

    • 名称(自定义类型 - 标签)

    • 模块和描述(隐藏,只读)

    问题是我的自定义类型标签呈现NAME属性为

    <span> role name </span>
    

    由于它不是“只读”,因此FORM组件可能会在POST中获得NAME .

    相反,只有ID被POST,因此FORM组件假定NAME为NULL .

    这导致CASE 2(3.2) - >创建关联,但用空字符串覆盖ROLE NAME .

    2.那么,这个变通办法有什么特别之处呢?

    2.1控制器

    这种解决方法非常简单 .

    在您的控制器中,在验证表单之前,您必须获取已发布的实体identyficators并获取匹配的实体,然后将它们设置为您的对象 .

    // example action
    public function createAction(Request $request)
    {      
        $em = $this->getDoctrine()->getEntityManager();
    
        // the workaround code is in updateUser function
        $user = $this->updateUser($request, new User());
    
        $form = $this->createForm(new UserType(), $user);
    
        if($request->getMethod() == 'POST') {
            $form->bindRequest($request);
    
            if($form->isValid()) {
                /* Persist, flush and redirect */
                $em->persist($user);
                $em->flush();
                $this->setFlash('avocode_user_success', 'user.flash.user_created');
                $url = $this->container->get('router')->generate('avocode_user_show', array('id' => $user->getId()));
    
                return new RedirectResponse($url);
            }
        }
    
        return $this->render('AvocodeUserBundle:UserManagement:create.html.twig', array(
          'form' => $form->createView(),
          'user' => $user,
        ));
    }
    

    并在updateUser函数中的变通方法代码下面:

    protected function updateUser($request, $user)
    {
        if($request->getMethod() == 'POST')
        {
          // getting POSTed values
          $avo_user = $request->request->get('avo_user');
    
          // if no roles are posted, then $owned_roles should be an empty array (to avoid errors)
          $owned_roles = (array_key_exists('avoRoles', $avo_user)) ? $avo_user['avoRoles'] : array();
    
          // foreach posted ROLE, get it's ID
          foreach($owned_roles as $key => $role) {
            $owned_roles[$key] = $role['id'];
          }
    
          // FIND all roles with matching ID's
          if(count($owned_roles) > 0)
          {
            $em = $this->getDoctrine()->getEntityManager();
            $roles = $em->getRepository('AvocodeUserBundle:Role')->findById($owned_roles);
    
            // and create association
            $user->setAvoRoles($roles);
          }
    
        return $user;
    }
    

    为此,您的SETTER(在本例中为User.php实体)必须是:

    public function setAvoRoles($avoRoles)
    {
        // first - clearing all associations
        // this way if entity was not found in POST
        // then association will be removed
    
        $this->getAvoRoles()->clear();
    
        // adding association only for POSTed entities
        foreach($avoRoles as $role) {
            $this->addAvoRole($role);
        }
    
        return $this;
    }
    

    3.最后的想法

    不过,我认为这种解决方法正在完成这项工作

    $form->bindRequest($request);
    

    应该做!这是我做错了,或者symfony的Collection表单类型不完整 .

    There are some major changes in Form component来自symfony 2.1,希望这将得到修复 .

    PS . 如果是我做错了什么......

    ...请发布应该做的方式!我很高兴看到一个快速,简单和“干净”的解决方案 .

    PS2 . 特别感谢:

    Jeppe Marianger-Lam和用户友好(来自IRC上的#symfony2) . 你一直非常乐于助人 . 干杯!

  • 8

    我得出的结论是,Form组件有问题,无法找到一种简单的方法来解决它 . 但是,我提出了一个完全通用的稍微麻烦的解决方案;它没有任何关于实体/属性的硬编码知识,因此将修复它遇到的任何集合:

    更简单,通用的解决方法

    这不要求您对实体进行任何更改 .

    use Doctrine\Common\Collections\Collection;
    use Symfony\Component\Form\Form;
    
    # In your controller. Or possibly defined within a service if used in many controllers
    
    /**
     * Ensure that any removed items collections actually get removed
     *
     * @param \Symfony\Component\Form\Form $form
     */
    protected function cleanupCollections(Form $form)
    {
        $children = $form->getChildren();
    
        foreach ($children as $childForm) {
            $data = $childForm->getData();
            if ($data instanceof Collection) {
    
                // Get the child form objects and compare the data of each child against the object's current collection
                $proxies = $childForm->getChildren();
                foreach ($proxies as $proxy) {
                    $entity = $proxy->getData();
                    if (!$data->contains($entity)) {
    
                        // Entity has been removed from the collection
                        // DELETE THE ENTITY HERE
    
                        // e.g. doctrine:
                        // $em = $this->getDoctrine()->getEntityManager();
                        // $em->remove($entity);
    
                    }
                }
            }
        }
    }
    

    在持久化之前调用新的cleanupCollections()方法

    # in your controller action...
    
    if($request->getMethod() == 'POST') {
        $form->bindRequest($request);
        if($form->isValid()) {
    
            // 'Clean' all collections within the form before persisting
            $this->cleanupCollections($form);
    
            $em->persist($user);
            $em->flush();
    
            // further actions. return response...
        }
    }
    
  • 6

    这就是我之前所做的 - 我不知道这是否是'正确'的方式,但它确实有效 .

    当您从提交的表单中获得结果时(即,在 if($form->isValid()) 之前或之后),只需询问角色列表,然后从实体中删除它们(将列表另存为变量) . 使用此列表,只需遍历它们,向存储库询问与ID匹配的角色实体,并在 persistflush 之前将这些实体添加到您的用户实体 .

    我刚刚搜索了Symfony2文档,因为我记得有关 prototype 表单集合的内容,并且出现了这个问题:http://symfony.com/doc/current/cookbook/form/form_collections.html - 它提供了如何正确处理javascript添加和删除表单中的集合类型的示例 . 也许首先尝试这种方法,然后尝试我之后提到的,如果你不能让它工作:)

相关问题