Конструктор форм Symfony2 Выбор в таблице соединений с метаданными

У меня есть 3 сущности:

[Member] ----OneToMany----> [MemberCategory] ---ManyToOne---> [Category]

Это хорошо работает для извлечения результатов из базы данных, но я не могу заставить Form Builder создать правильные элементы управления формой.

Мне нужен список всех категорий с флажками, которые отмечены для тех категорий, которые используются участником. В конце концов я хочу добавить поле приоритета.

Член

class Member
{
    protected $id;

    @ORM\OneToMany(targetEntity="MemberCategory", mappedBy="member")
    protected $categories;
}

Категория

class Category
{
    protected $id;

    @ORM\Column(name="category_name", type="string", length=50, nullable=false, unique=true)
    private $categoryName;
}

Категория участников

class MemberCategory
{
    @ORM\Id
    @ORM\ManyToOne(targetEntity="Member")
    @ORM\JoinColumns({
    @ORM\JoinColumn(name="member_id", referencedColumnName="id", onDelete="CASCADE")
    private $member;

    @ORM\Id
    @ORM\ManyToOne(targetEntity="Category")
    @ORM\JoinColumns({
    @ORM\JoinColumn(name="category_id", referencedColumnName="id", onDelete="CASCADE")
    private $category;

    @ORM\Column(name="priority", type="integer", nullable=true)
    protected $priority;
}

Очевидные попытки с конструктором форм, которые не работают.

Если я использую:

$builder->add('categories', 'entity', array(
    'class'        => 'SMWMemberBundle:Category',
    'property'     => 'categoryName',
    'multiple'     => true,
    'expanded'     => true,
    'required'     => false
));

Я получаю выбор со всеми категориями, но ни одна из выбранных в MemberCategory для этого члена.

Если я использую:

$builder->add('categories', 'entity', array(
    'class'        => 'SMWMemberBundle:MemberCategory',
    'property'     => 'category.categoryName',
    'multiple'     => true,
    'expanded'     => true,
    'required'     => false
));

Я получаю все выбранные категории для всех пользователей.

Кто-нибудь знает, как заставить это работать, это очевидный общий шаблон в реляционных данных, и его было бы легко использовать с помощью SQL и PHP.

Есть ли прямое решение в Symfony 2.3 и Doctrine?


person Peter Wooster    schedule 29.07.2013    source источник


Ответы (3)


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

Сначала создайте тип формы MemberCategory, соответствующий вашим потребностям:

<?php    
namespace Company\YourBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class MemberCategoryType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('priority')
            ->add('category', 'entity',
                    array('property' => 'name',
                        'class' => 'CompanyYourBundle:Category'))
        ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Company\YourBundle\Entity\MemberCategory'
        ));
    }

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

Затем добавьте этот тип формы в форму типа Member:

<?php

namespace Company\YourBundle\Form\Type;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;

class MemberType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('name')
            ->add('members', 'collection', array(
                'type' => new MemberCategoryType(),
                'allow_add' => true,
                'allow_delete' => true,
                'by_reference' => false,));
        ;
    }

    public function setDefaultOptions(OptionsResolverInterface $resolver)
    {
        $resolver->setDefaults(array(
            'data_class' => 'Company\YourBundle\Entity\Member'
        ));
    }

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

Затем вы можете следовать документации, чтобы добавить столько категорий, сколько хотите. ваших членов и назначение приоритета каждый раз.

person cheesemacfly    schedule 29.07.2013
comment
Документация делает вид, что добавление и удаление категорий будет огромным количеством проблем, даже с использованием кода на стороне клиента. Так ли это, или я неправильно читаю документы? - person Peter Wooster; 29.07.2013
comment
Вы правильно прочитали! Я так и сделал, хотя не уверен, что это лучший способ. Я также рассмотрю другие ответы, потому что я согласен с вами в том, что это распространенный шаблон без какой-либо четкой документации на официальном сайте. - person cheesemacfly; 29.07.2013
comment
Спасибо, документы Doctrine описывают, как справиться с этим со стороны объекта в docs.doctrine-project.org/projects/doctrine-orm/en/latest/ (см. вариант использования 3). Но они ничего не говорят об использовании этого с формой Symfony. - person Peter Wooster; 29.07.2013

Вам нужно будет использовать конструктор запросов внутри вашей формы

$builder->add('categories', 'entity', array(
        'class' => 'SMWMemberBundle:MemberCategory',
        'property'     => 'category.categoryName',
        'query_builder' => function(EntityRepository $er ) use ( ? ) {
           return $er->createQueryBuilder( ? )

          // your query with a left join probably

         }
        'multiple'     => true,
        'expanded'     => true,
        'required' => false
    ));

См. документацию для правильного использования. см. построитель запросов формы symfony2 с параметрами для примера

person Pierrito    schedule 29.07.2013
comment
Пожалуйста, завершите свой ответ, так как основная часть закомментирована в вашем ответе - person Pmpr; 29.11.2016

Пост cheesemacfly не так уж и плох, но он называется встроенной формой и может быть сложным в управлении. На самом деле вам просто нужно создать «Пользовательские репозитории» http://symfony.com/doc/current/book/doctrine.html#custom-repository-classes

Он добавит новые методы для «захвата» вашего объекта ORM с помощью Doctrine2, например find() или findBy();

1) Создайте новый репозиторий в папке репозитория вашего пакета.

<?php
  namespace YourVendor\SMWMemberBundle\Repository;
  use Doctrine\ORM\EntityRepository;

  class CategoryRepository extends EntityRepository{

  public function UsedByMember($member){
  return $this
         ->createQueryBuilder('c')
         ->leftJoin('c.Member', 'mc')
         ->where('mc.member = ?1')
         ->setParameter(1, $member);
}

}

2) Прикрепите ваш репозиторий Cutom к вашей сущности

namespace YourVendor\SMWMemberBundle\Entity;
use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity(repositoryClass="YourVendor\SMWMemberBundle\Entity\ProductRepository")
* @ORM\Table(name="Category") 
*/
class Category{

3) добавьте конструктор в свой класс Form и передайте Entity Manager и переменную, необходимую для вашего запроса:

class CategoryUserForm extends AbstractType
{
   private $em;
   private $member ;


   public function __construct(EntityManager $em, $site, $seed)
   {
       $this->em = $em;
       $this->member = $member;

   }

public function buildForm(FormBuilder $builder, array $options)
{
    $qb = $this->em->getRepository('SMWMemberBundle:Category')->UsedByUsers($this->member);
    $builder->add('categories', 'entity', array(
        'class' => 'SMWMemberBundle:MemberCategory',
        'query_builder'     => $qb,
        'multiple'     => true,
        'expanded'     => true,
        'required' => false
    ));
 }

4) В вашем контроллере вы создаете свою форму следующим образом:

$editForm = $this->createForm(new CategoryUserForm($em, $member), $category);

Не стесняйтесь задавать мне вопросы, я надеюсь, что это то, что вы ищете ;)

person Luna    schedule 29.07.2013