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

1. Быстрый обзор

1.1 Цель

Я пытаюсь создать пользовательский инструмент для создания /редактирования. Редактируемые поля:

  • имя пользователя (тип: текст)
  • plainPassword (тип: пароль)
  • электронная почта (тип: электронная почта)
  • groups (type: collection)
  • avoRoles (тип: коллекция)

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

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

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

  1. Форма пользователя
  2. Таблица с отображением $ userRepository-> findAllRolesExceptOwnedByUser ($ user);

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

1.3 Желаемая функциональность

1.3.1 Добавить роль:

     КОГДА  пользователь нажимает кнопку «+» (добавить) в таблице ролей
     THEN  jquery удаляет эту строку из таблицы ролей
     AND  jquery добавляет новый элемент списка в форму пользователя (список списков исключений)

1.3.2 Удаление ролей:

     КОГДА  пользователь нажимает кнопку «x» (удалить) в форме пользователя (список списков исключений)
     THEN  jquery удаляет этот элемент списка из формы пользователя (список списков исключений)
     AND  jquery добавляет новую строку в таблицу ролей

1.3.3 Сохранить изменения:

     КОГДА  пользователь нажимает кнопку «Записать» (сохранить)
     ТОГДА  пользовательская форма отправляет все поля (имя пользователя, пароль, адрес электронной почты, avoRoles, группы)
     И  сохраняет avoRoles как ArrayCollection объектов Role (отношение ManyToMany)
     AND  сохраняет группы как ArrayCollection объектов Role (отношение ManyToMany)

Примечание. Пользователю могут быть назначены ТОЛЬКО существующие роли и группы. Если по какой-либо причине они не найдены, форма не должна проверяться.


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

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

Предполагается, что эта форма будет отображаться:

  • скрытый идентификатор роли
  • Имя роли (ТОЛЬКО ЧИТАЙТЕ)
  • скрытый модуль (ТОЛЬКО ЧИТАЙТЕ)
  • скрытое описание (только для чтения)
  • кнопка удаления (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. Во-первых, потому что я не хочу создавать новую роль. Я хочу просто создать связь между существующими ролями и сущностями пользователя.
  2. Даже если я действительно хочу создать новую роль, ее идентификатор должен быть сгенерирован автоматически:

    /**

    • @ORM \ Id
    • @ORM \ Колонка (тип = "целое число") литий>
    • @ORM \ GeneratedValue (стратегия = "AUTO") * / защищенный $ id;

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

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

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

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

  1. Новый пользователь создан
  2. Новому пользователю назначена роль с назначенным ему идентификатором (ууу!)
  3. но имя этой роли перезаписываетсяпустая строка (облом!)

Очевидно, это не то, что я хочу. Я не хочу редактировать /перезаписывать роли. Я просто хочу добавить связь между ними и пользователем.

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 (без установщика идентификаторов), либо CASE2 (с установщиком идентификаторов).

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;

И изменив by_reference на false в 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: 6fb81861d84d460a08087087087087087087087087087087087087087087087081808708780870870878087087808708708708708780818087808708708780870878087087087

5. Резюме

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

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

67 голосов | спросил ioleo 18 J0000006Europe/Moscow 2012, 23:40:25

5 ответов


0

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

Я создал набор расширений форм для symfony2 (см. FormExtensionsBundle в github), и они включить тип формы для обработки отношений One /Many ToMany .

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

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

Посмотрите на EventListener /CollectionUploadListener.php файл, чтобы увидеть, как мы обрабатываем это сейчас.

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

ответил ioleo 13 J0000006Europe/Moscow 2014, 14:17:21
0

Я пришел к тому же выводу, что с компонентом 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...
    }
}
ответил RobMasters 10 +04002012-10-10T18:48:03+04:00312012bEurope/MoscowWed, 10 Oct 2012 18:48:03 +0400 2012, 18:48:03
0

1. Обходной путь решения

Обходное решение, предложенное Jeppe Marianger-Lam, на данный момент является единственной из известных мне работ.

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

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

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

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

    & Lt; & продолжительность GT; имя роли </span>

И поскольку он не был "только для чтения", компонент FORM должен получить NAME в POST.

Вместо этого был помещен только ID, поэтому компонент 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's Collection не полный.

. некоторые важные изменения в компоненте Form появятся в Symfony 2.1, надеюсь, это будет исправлено.

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

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

PS2. Особая благодарность:

Jeppe Marianger-Lam и удобный для пользователя (из # symfony2 на IRC). Вы были очень полезны. Ура!

ответил ioleo 19 J0000006Europe/Moscow 2012, 13:03:47
0

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

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

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

ответил Jeppe Mariager-Lam 19 J0000006Europe/Moscow 2012, 00:04:09
0

Вам нужно еще несколько объектов:
ПОЛЬЗОВАТЕЛЬ
id_user (тип: целое число)
имя пользователя (тип: текст)
plainPassword (тип: пароль)
электронная почта (тип: электронная почта)


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


AVOROLES
id_avorole (тип: целое число)
описание (тип: текст)


* user_group *
id_user_group (тип: целое число)
id_user (тип: целое число) (это идентификатор объекта пользователя)
id_group (тип: целое число) (это идентификатор объекта группы)


* USER_AVOROLES *
id_user_avorole (тип: целое число)
id_user (тип: целое число) (это идентификатор объекта пользователя)
id_avorole (тип: целое число) (это идентификатор объекта avorole)


Например, вы можете получить что-то вроде этого:
Пользователь:
id: 3
имя пользователя: john
plainPassword: johnpw
электронная почта: [email protected]


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


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

ответил user1895187 21 42013vEurope/Moscow11bEurope/MoscowThu, 21 Nov 2013 17:26:12 +0400 2013, 17:26:12

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132