web-dev-qa-db-ja.com

エンティティのSymfony2コレクション-既存のエンティティとの関連付けを追加/削除する方法は?

1.簡単な概要

1.1目標

私が達成しようとしているのは、ユーザーの作成/編集ツールです。編集可能なフィールドは次のとおりです。

  • ユーザー名(タイプ:テキスト)
  • plainPassword(タイプ:パスワード)
  • 電子メール(タイプ:電子メール)
  • グループ(タイプ:コレクション)
  • avoRoles(タイプ:コレクション)

注:最後のプロパティには名前がありません$ rolesそれらを避けるために、ロールのコレクションを$ avoRolesの下に保存することにしました。

1.2ユーザーインターフェース

マイテンプレート は2つのセクションで構成されます。

  1. ユーザーフォーム
  2. $ userRepository-> findAllRolesExceptOwnedByUser($ user);を表示するテーブル。

注:findAllRolesExceptOwnedByUser()はカスタムリポジトリ関数であり、すべてのロール(まだ$ userに割り当てられていないロール)のサブセットを返します。

1.3必要な機能

1.3.1役割の追加:

いつ ユーザーが役割テーブルの[+](追加)ボタンをクリックします
 その後 jqueryは、Rolesテーブルからその行を削除します
 そして  jqueryは、ユーザーリスト(avoRolesリスト)に新しいリストアイテムを追加します
 

1.3.2ロールの削除:

いつ ユーザーがユーザーフォームの[x](削除)ボタンをクリックする(avoRolesリスト)
 その後 jqueryは、ユーザーフォーム(avoRolesリスト)
からそのリストアイテムを削除します そして  jqueryは、ロールテーブルに新しい行を追加します
 

1.3.3変更を保存します。

いつ ユーザーが[Zapisz](保存)ボタンをクリックします
 その後 ユーザーフォームはすべてのフィールド(ユーザー名、パスワード、メール、avoRoles、グループ)を送信します
 そして  avoRolesをRoleエンティティのArrayCollection(ManyToMany関係)として保存します
 そして  グループをロールエンティティのArrayCollectionとして保存します(ManyToMany関係)
 

注:既存のロールとグループのみをユーザーに割り当てることができます。何らかの理由で見つからない場合、フォームは検証されません。


2.コード

このセクションでは、このアクションの背後にあるコードを紹介します。説明が十分ではなく、コードを表示する必要がある場合は教えてください。貼り付けます。不要なコードでスパムを送信することを避けるため、そもそもすべてを貼り付けているわけではありません。

2.1ユーザークラス

My 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役割クラス

My 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)ボタン

モジュールと説明は非表示フィールドとして表示されます。管理者がユーザーからロールを削除すると、そのロールが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. 最初の理由は、新しい役割を作成したくないからです。既存のRoleエンティティとUserエンティティの間に関係を作成したいだけです。
  2. 新しいロールを作成したい場合でも、そのIDは自動生成される必要があります。

    / **

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

3.2ケース2:ロールエンティティにIDプロパティのセッターを追加

それは間違っていると思いますが、念のためにやったのです。このコードを役割エンティティに追加した後:

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

新しいユーザーを作成してロールを追加すると、保存...どうなりますか:

  1. 新しいユーザーが作成されます
  2. 新しいユーザーには、目的のIDが割り当てられたロールがあります(はい!)
  3. しかし、そのロールの名前は空の文字列で上書きされます(残念!)

明らかに、それは私が望むものではありません。役割を編集/上書きしたくありません。それらとユーザーの間に関係を追加したいだけです。

3.3ケース3: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:ユーザーフレンドリーが示唆するとおり

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_referencefalseに変更します:

// ...

                ->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. ユーザーとロールの関連付けはnot createdでした
  2. ..しかし、ロールエンティティの名前は空の文字列で上書きされました(3.2のように)

だから..それは何かを変えましたが、間違った方向に。

4.バージョン

4.1 Symfony2 v2.0.15

4.2 Doctrine2 v2.1.7

4.3 FOSUserBundleバージョン: 6fb81861d84d460f1d070ceb8ec180aac841f7fa

5.まとめ

私は多くの異なるアプローチを試しましたが(上記は最新のもののみです)、コードを勉強し、グーグルで検索し、答えを探すことに何時間も費やした後、私はこれを動作させることができませんでした。

どんな助けも大歓迎です。何かを知る必要がある場合は、必要なコードの部分を投稿します。

67
ioleo

それで一年が経ち、この質問は非常に一般的になりました。それ以来、symfonyは変化し、私のスキルと知識も向上し、この問題に対する私の現在のアプローチも変化しました。

Symfony2の一連のフォーム拡張機能を作成しました(githubの FormExtensionsBundle プロジェクトを参照)。One/ ManyToMany関係を処理するためのフォームタイプが含まれています。

これらを書いている間、コレクションを処理するためにコントローラーにカスタムコードを追加することは受け入れられません-フォームの拡張機能は使いやすく、すぐに使用でき、開発者の負担を軽減するものでした。また、覚えておいてください。

そのため、アソシエーションの追加/削除コードを他の場所に移動する必要がありました-それを行う適切な場所は、当然EventListenerでした:)

EventListener/CollectionUploadListener.php ファイルを見て、これをどのように処理するかを確認してください。

PS。ここでコードをコピーする必要はありません。最も重要なことは、そのようなものを実際にEventListenerで処理することです。

10
ioleo

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...
    }
}
_
13
RobMasters

1.回避策

Jeppe Marianger-Lamが提案した回避策は、現時点で私が知っている唯一の方法です。

1.1私の場合、なぜ機能しなくなったのですか?

RoleNameTypeを(他の理由で)変更しました:

  • ID(非表示)
  • 名前(カスタムタイプ-ラベル)
  • モジュールと説明(非表示、読み取り専用)

問題は、カスタムプロパティラベルがNAMEプロパティを

 
 <span>ロール名</ span> 
 

また、「読み取り専用」ではなかったため、FORMコンポーネントはPOSTでNAMEを取得することが期待されていました。

代わりに、IDのみがPOSTされたため、FORMコンポーネントはNAMEがNULLであると想定しました。

これにより、ケース2(3.2)->関連付けが作成されますが、ROLE NAMEは空の文字列で上書きされます。

2.では、この回避策は正確に何ですか?

2.1コントローラー

この回避策は非常に簡単です。

コントローラーで、フォームを検証する前に、投稿されたエンティティー識別子を取得し、一致するエンティティーを取得してから、オブジェクトに設定する必要があります。

// 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フォームタイプが完全ではありません。

Formコンポーネントにいくつかの大きな変更があります symfony 2.1で導入される予定です。うまくいけば修正されるでしょう。

PS。私が何か間違ったことをしているなら...

...それが行われるべき方法を投稿してください!迅速で簡単な「クリーン」なソリューションを見つけてうれしいです。

PS2。に感謝します:

Jeppe Marianger-Lamとユーザーフレンドリー(IRCの#symfony2から)。とても助かりました。乾杯!

8
ioleo

これは私が以前にやったことです-それがそれを行うための「正しい」方法であるかどうかはわかりませんが、動作します。

提出されたフォームから結果を取得するとき(つまり、if($form->isValid())の直前または直後)、ロールのリストを確認し、それらをすべてエンティティから削除します(リストを変数として保存します)。このリストを使用して、すべてをループし、IDに一致するロールエンティティをリポジトリに要求し、persistおよびflushの前にこれらをユーザーエンティティに追加します。

フォームコレクションのprototypeについて何か覚えていたため、Symfony2のドキュメントを検索したところ、次のようになりました。 http://symfony.com/doc/current/cookbook/form/form_collections.html -フォームのコレクションタイプのjavascriptの追加と削除を正しく処理する方法の例があります。おそらく最初にこのアプローチを試して、次にそれを動作させることができない場合は、上で述べたものを試してください:)

6

さらにエンティティが必要です:
[〜#〜] user [〜#〜]
id_user(タイプ:整数)
ユーザー名(タイプ:テキスト)
plainPassword(タイプ:パスワード)
電子メール(タイプ:電子メール)


[〜#〜] groups [〜#〜]
id_group(タイプ:整数)
説明(タイプ:テキスト)


[〜#〜] avoroles [〜#〜]
id_avorole(タイプ:整数)
説明(タイプ:テキスト)


* SER_GROUP *
id_user_group(タイプ:整数)
id_user(type:integer)(これはユーザーエンティティのIDです)
id_group(type:integer)(これはグループエンティティのIDです)


* SER_AVOROLES *
id_user_avorole(タイプ:整数)
id_user(type:integer)(これはユーザーエンティティのIDです)
id_avorole(type:integer)(これはavoroleエンティティのIDです)


たとえば、次のようなものを使用できます。
ユーザー:
id:3
ユーザー名:john
plainPassword:johnpw
email:[email protected]


グループ:
id_group:5
説明:グループ5


ユーザー・グループ:
id_user_group:1
id_user:3
id_group:5
*このユーザーは多くのグループを持つことができるため、別の行に*

0
user1895187