Коллекция сущностей Symfony2 - как добавить/удалить связь с существующими сущностями?


1. Краткий обзор

1.1 Цель

То, чего я пытаюсь достичь, - это инструмент создания/редактирования пользователя. Редактируемые поля:

  • имя пользователя (тип: текст)
  • Обычный пароль (тип: пароль)
  • электронная почта (тип: электронная почта)
  • группы (тип: коллекция)
  • Аворолы (тип: коллекция)

Примечание: последнее свойство не называется $роли, потому что мой класс пользователя расширяет класс пользователя FOSUserBundle и перезаписывает роли приносили больше проблем. Чтобы избежать их, я просто решил сохранить свою коллекцию ролей под $Avoroles.

1.2 Пользовательский интерфейс

Мой шаблон состоит из 2 разделов:

  1. Форма пользователя
  2. Таблица, отображающая $UserRepository->findallrolesexceptownedbyuser($пользователь);

Примечание: findAllRolesExceptOwnedByUser() - это пользовательская функция репозитория, возвращающая подмножество всех ролей (тех, которые еще не назначены $пользователь).

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 Класс пользователя

Мой класс пользователя расширяет класс пользователя 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 Пользовательские репозитории

Нет необходимости публиковать это, так как они работают просто отлично - они возвращают подмножество всех ролей/групп (тех, которые не назначены пользователю).

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 Тип роли

Эта форма должна отображать:

  • идентификатор скрытой роли
  • Имя роли (ТОЛЬКО ДЛЯ ЧТЕНИЯ)
  • скрытый модуль (ТОЛЬКО ДЛЯ ЧТЕНИЯ)
  • скрытый описание (ТОЛЬКО ДЛЯ ЧТЕНИЯ)
  • удалить кнопку (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()"?

Но установщик идентификатора должен не требуется.

  1. Во-первых, потому что я не хочу создавать НОВУЮ роль. Я хочу просто создать связь между существующими сущностями Роли и пользователя.
  2. Даже если бы я хотел создать новую роль, ее идентификатор должен быть сгенерирован автоматически:

    /**

    • @ORM\Идентификатор
    • @ORM\Столбец (тип= "целое число")
    • @ORM\Генерируемое значение (стратегия="АВТО") */ защищенный идентификатор $;

3.2 Случай 2: добавлен параметр для свойства идентификатора в роли сущность

Я думаю, что это неправильно, но я сделал это просто для уверенности. После добавления этого кода в сущность роли:

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

Если я создам нового пользователя и добавлю роль, то СОХРАНЮ... Что происходит:

  1. Создан новый пользователь
  2. У нового пользователя есть роль с назначенным желаемым идентификатором (ура!)
  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;
    }

К сожалению, это ничего не меняет.. результаты либо СЛУЧАЙ1 (без задатчика идентификатора), либо СЛУЧАЙ 2 (с настройщиком идентификаторов).

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;

И изменение по_референции на 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, кое-что изменило:

  1. Связь между пользователем и ролью не была создана
  2. .. но имя сущности роли было перезаписано пустой строкой (как в 3.2)

Итак... это действительно что-то изменило, но в неправильном направлении.

4. Версии

4.1 Symfony2 v2.0.15

4.2 Доктрина2 v2.1.7

Версия 4.3 FOSUserBundle: 6fb81861d84d460f1d070ceb8ec180aac841f7fa

5. Краткое описание

Я перепробовал множество различных подходов (выше приведены только самые последние), и после нескольких часов, потраченных на изучение кода, поиск в Google и поиск ответа, я просто не смог заставить это работать.

Любая помощь будет очень признательна. Если вам нужно что-то узнать, я опубликую любую часть кода, которая вам нужна.

Author: ioleo, 2012-06-18

5 answers

Я пришел к тому же выводу, что с компонентом формы что-то не так, и не вижу простого способа это исправить. Тем не менее, я придумал несколько менее громоздкое обходное решение, которое является полностью универсальным; в нем нет никаких жестко запрограммированных знаний о сущностях/атрибутах, поэтому он исправит любую коллекцию, с которой столкнется:

Более простой, универсальный метод обхода

Это не требует от вас внесения каких-либо изменений в вашу сущность.

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
Author: RobMasters, 2012-10-10 14:48:03

Итак, прошел год, и этот вопрос стал довольно популярным. С тех пор Symfony изменилась, мои навыки и знания также улучшились, как и мой нынешний подход к этой проблеме.

Я создал набор расширений форм для symfony2 (см. Проект formextensionsbundle на github), и они включают тип формы для обработки одного/многих отношений между множеством .

При их написании добавление пользовательского кода в ваш контроллер для обработки коллекций было неприемлемо - расширения форм должны были быть простыми в использовании, работать "из коробки" и облегчать жизнь нам, разработчикам, а не усложнять. Также... помни.. СУХОЙ!

Поэтому мне пришлось переместить код добавления/удаления ассоциаций в другое место - и подходящим местом для этого, естественно, был список событий:)

Взгляните на EventListener/CollectionUploadListener.php файл, чтобы посмотреть, как мы справимся с этим сейчас.

PS. Копирование кода здесь не нужно, самое главное дело в том, что подобные вещи на самом деле должны обрабатываться в списке событий.

 10
Author: ioleo, 2015-12-30 14:16:46

1. Обходное решение

Обходное решение, предложенное Йеппе Мариангер-Ламом, на данный момент является единственным работающим, о котором я знаю.

1.1 Почему это перестало работать в моем случае?

Я изменил свой тип роли (по другим причинам) на:

  • Идентификатор (скрытый)
  • имя (пользовательская метка типа)
  • модуль и описание (скрытый, только для чтения)

Проблема заключалась в том, что моя пользовательская метка типа отображала свойство NAME как


    <span> role name </span>

И поскольку он не был "только для чтения", компонент ФОРМЫ ожидал получить ИМЯ в СООБЩЕНИИ.

Вместо этого был опубликован только идентификатор, и, следовательно, предполагаемое ИМЯ компонента ФОРМЫ равно НУЛЮ.

Это приводит к СЛУЧАЮ 2 (3.2) ->создание ассоциации, но перезапись ИМЕНИ РОЛИ пустой строкой.

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

Для этого, чтобы ваш СЕТТЕР работал (в данном случае в 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, надеюсь, это будет исправлено.

ПС. Если это я делаю что-то не так...

... пожалуйста, напишите, как это должно быть сделано! Я был бы рад увидеть быстрое, простое и "чистое" решение.

ПС2. Особая благодарность:

Йеппе Мариангер-Лам и дружелюбный к пользователям (из #symfony2 в IRC). Вы мне очень помогли. Ваше здоровье!

 8
Author: ioleo, 2016-02-13 09:47:17

Это то, что я делал раньше - я не знаю, является ли это "правильным" способом сделать это, но это работает.

Когда вы получите результаты из отправленной формы (т.Е. Непосредственно перед или сразу после if($form->isValid())), просто запросите список ролей, а затем удалите их все из сущности (сохранив список в качестве переменной). С помощью этого списка просто просмотрите их все, запросите в репозитории сущность роли, соответствующую идентификаторам, и добавьте их в свою сущность пользователя перед тем, как persist и flush.

Я просто просмотрел документацию Symfony2, потому что я вспомнил кое-что о prototype для коллекций форм, и вот что получилось: http://symfony.com/doc/current/cookbook/form/form_collections.html - В нем есть примеры того, как правильно работать с javascript, добавляя и удаляя типы коллекций в формах. Возможно, сначала попробуйте этот подход, а затем попробуйте то, о чем я упоминал выше, если вы не сможете заставить его работать:)

 6
Author: Jeppe Mariager-Lam, 2012-12-08 11:50:16

Вам нужна более лиц:
пользователь
id_user (тип: число)
имя пользователя (тип: текст)
Обычный пароль (тип: пароль)
электронная почта (тип: электронная почта)


ГРУППЫ
id_group (тип: целое число)
описание (тип: текст)


АРОМАТИЗАТОРЫ
id_avorole (тип: целое число)
описание (тип: текст)


* ПОЛЬЗОВАТЕЛЬ_ГРУППА*
идентификатор_пользователя_группы (тип:целое число)
id_user (тип: целое число) (это идентификатор сущности пользователя)
id_group (тип: целое число) (это идентификатор сущности группы)


* ПОЛЬЗОВАТЕЛЬ_АВОРОЛЫ*
id_user_avorole (тип:целое число)
id_user (тип: целое число) (это идентификатор сущности пользователя)
id_avorole (тип: целое число) (это идентификатор сущности avorole)


У вас может быть, например, что-то вроде этого:
пользователь:
идентификатор: 3
имя пользователя: джон
Простой пароль: джонпв
электронная почта: [email protected]


группа:
id_group: 5
описание: группа 5


группа пользователей:
id_user_group: 1
id_user: 3
id_group: 5
*у этого пользователя может быть много групп, поэтому в другой строке *

 0
Author: user1895187, 2013-11-21 13:26:12