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

2019-06-25 13:57发布

1.快速概览

1.1目标

我正在努力实现一个创建/编辑用户工具。 可编辑的字段有:

  • 用户名(类型:文本)
  • plainPassword(类型:密码)
  • 电子邮件(类型:电子邮件)
  • 组(类型:集合)
  • avoRoles(类型:集合)

注意:最后一个属性没有命名becouse我的User类$角色是延长FOSUserBundle的用户类别和覆盖角色带来更多的问题。 为了避免他们,我干脆决定来存储我在$ avoRoles角色的集合。

1.2用户接口

我的模板分为2个部分:

  1. 用户表单
  2. 表显示$ userRepository-> findAllRolesExceptOwnedByUser($用户);

注: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.代码

在这一节中,我介绍/或简短描述这一行动背后的代码。 如果说明是不够的,你需要看到的代码只是告诉我,我会粘贴。 我不pasteing这一切摆在首位,以避免不必要代码滥发你。

2.1用户类

我的用户类扩展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级的角色

我的角色类扩展Symfony的安全组件的核心角色类。

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自定义库

这不是neccesary发布此,因为他们工作得很好 - 他们返回所有角色的一个子集/组(那些未分配给用户)。

2.6用户等级和积分

用户类型:

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)的按钮

模块和描述都呈现为隐藏字段,becouse当管理员删除来自用户的角色,该角色应的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二传手不应要求。

  1. 首先becouse我不希望创建一个新的角色。 我只想创造现有的角色和用户实体之间的关系。
  2. 即便我想创建一个新的角色,它的ID应该是自动生成的:

    / **

    • @ORM \标识
    • @ORM \柱(类型= “整数”)
    • @ORM \ generatedValue(策略= “AUTO”)* /保护的$ id;

3.2情况2:在角色实体添加设置器ID属性

我认为这是错误的,但我之所以这样做是可以肯定的。 将此代码添加到角色的实体后:

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

如果我创建新用户,并添加一个角色,然后保存...发生了什么:

  1. 创建新用户
  2. 新用户与指定所需的ID角色(耶!)
  3. 但该角色的名字改写为空字符串 (无赖!)

很显然,那不是我想要的。 我不想编辑/覆写角色。 我只想补充他们与用户之间的关系。

3.3案例3:解决方法由叶普建议

当我第一次遇到这个问题,我结束了一个解决办法,同样是叶普建议。 今天(其他原因),我不得不重拍我的表单/视图和解决方法停止工作。

在情形3 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的setter)或CASE2(ID为二传手)。

3.4案例4:通过用户友好的建议

加入级联= {“持续”,“删除”}来映射。

/**
 * @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;

而量变到质变by_reference在FormType 错误

// ...

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

// ...

而在3.3建议保留区代码没有改变的东西:

  1. 用户和角色之间的关联未创建
  2. ..但角色实体的名称是由空字符串覆盖(如3.2)

所以..它确实改变了一些事,但在错误的方向。

4.版本

4.1 Symfony2的v2.0.15

4.2 Doctrine2 v2.1.7

4.3 FOSUserBundle版本: 6fb81861d84d460f1d070ceb8ec180aac841f7fa

5.总结

我已经尝试了许多不同势的方法(以上所述仅是最近的)和花在学习代码,google'ing和寻找答案我只是无法得到这个工作小时后。

任何帮助将不胜感激。 如果你需要知道什么,我会发布任何你需要的代码的一部分。

Answer 1:

我得出了同样的结论,即有一些错误的表单组件,并不能看到一个简单的方法来解决它。 不过,我想出了一个略少麻烦的解决方法解决方案是完全通用的; 它没有实体的任何硬编码的知识/属性,以便将修复任何集合它遇到:

简单,通用的变通办法

这并不需要你做你的实体的任何变化。

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...
    }
}


Answer 2:

所以,一年过去了,这个问题已经变得相当普遍。 Symfony的改变,因为我的技能和知识,也提高了,所以有我目前的做法这个问题。

我创建了一套Symfony2的形式扩展(见FormExtensionsBundle在github上的项目),他们包括handleing单/多一对多关系的表单类型。

在写这些,添加自定义代码到你的控制器来处理的集合是不能接受 - 形式扩展了应该是很容易使用,工作外的开箱,并就我们的生活更轻松的开发,而不是更辛苦。 还记得.. ..干的!

所以,我不得不搬到添加/删除关联代码别的地方 - 而要做到这自然是一个事件侦听正确的地方:)

看一看的事件监听/ CollectionUploadListener.php文件,看看现在我们是如何处理这个问题。

PS。 这里复制的代码是不必要的,最重要的事情就是这样的东西实际上应该在事件监听处理。



Answer 3:

1.解决方法解决

由叶普Marianger林提出的解决方法解决方案是目前唯一一个工作,我知道的。

1.1为什么没有制止我的案子?

我改变了我的RoleNameType(其他原因):

  • ID(隐藏)
  • 名称(自定义类型 - 标签)
  • 模块及描述(隐藏,只读)

这个问题是我的自定义类型的标签渲染Name属性


    <span> role name </span>

而且,由于它不是“只读”有望获得NAME在POST表单组件。

代替只ID被张贴,并由此形成组件假定NAME是NULL。

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

2.那么,什么exacly是此解决办法呢?

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中的收藏表单类型是不完整的。

有在表单组件一些重大变化 symfony的2.1 comeing,希望这将是固定的。

PS。 如果是我做错了什么?

...请张贴它应该做的方式! 我很高兴地看到一个快捷,方便和“干净”的解决方案。

PS2。 特别感谢:

叶普Marianger-Lam和用户友好(从#Symfony2的上IRC)。 你帮了大忙。 干杯!



Answer 4:

这是我以前做 - 我不知道这是否是“正确”的方式做到这一点,但它的作品。

当您从提交的表单获得满意的结果(即之前或之后的权利if($form->isValid())简单地问了角色的列表,然后从实体全部删除(保存列表作为变量)。 有了这个名单,只是通过他们所有的回路,要求相匹配的ID的角色实体存储库,而这些你以前添加到您的用户实体persistflush

我只是通过Symfony2的文档搜索,因为我想起了一些关于prototype的形式集合,并且此止跌回升: http://symfony.com/doc/current/cookbook/form/form_collections.html -它有一个如何正确处理实例用JavaScript添加和集合类型的表单中删除。 也许是第一次尝试这种方法,然后尽我上面提到的以后,如果你不能得到它的工作:)



Answer 5:

你需要一些更多的实体:
用户
id_user(类型:整数)
用户名(类型:文本)
plainPassword(类型:密码)
电子邮件(类型:电子邮件)



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
用户名:约翰
plainPassword:johnpw
电子邮件:john@email.com


组:
id_group:5
descripcion:组5


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



文章来源: Symfony2 collection of Entities - how to add/remove association with existing entities?