2

我正在使用 Symfony 2.1 和 Doctrine 2。

我正在处理 2 个主要实体:Place 和 Feature,它们之间具有 ManyToMany 关系。数据库中有许多特征,为了按主题对它们进行分组,特征还与具有 ManyToOne 关系的 FeatureCategory 实体相关。

这是不同实体的代码:

地方实体_

namespace Mv\PlaceBundle\Entity;
…

/**
 * Mv\PlaceBundle\Entity\Place
 *
 * @ORM\Table(name="place")
 * @ORM\Entity(repositoryClass="Mv\PlaceBundle\Entity\Repository\PlaceRepository")
 * @ORM\HasLifecycleCallbacks
 */
class Place
{
  /**
   * @var integer $id
   *
   * @ORM\Column(name="id", type="integer")
   * @ORM\Id
   * @ORM\GeneratedValue(strategy="AUTO")
   */
  private $id;

  /**
   * @var string $name
   *
   * @ORM\Column(name="name", type="string", length=255, unique=true)
   * @Assert\NotBlank
   */
  private $name;

  /**
   * @ORM\ManyToMany(targetEntity="\Mv\MainBundle\Entity\Feature")
   * @ORM\JoinTable(name="places_features",
   *    joinColumns={@ORM\JoinColumn(name="place_id", referencedColumnName="id")},
   *    inverseJoinColumns={@ORM\JoinColumn(name="feature_id", referencedColumnName="id")}
   * )
   */
  private $features;

  /**
   * Get id
   *
   * @return integer 
   */
  public function getId()
  {
    return $this->id;
  }

  /**
   * Set name
   *
   * @param string $name
   * @return Place
   */
  public function setName($name)
  {
    $this->name = $name;
    return $this;
  }

  /**
   * Get name
   *
   * @return string 
   */
  public function getName()
  {
    return $this->name;
  }

  /**
   * Add features
   *
   * @param \Mv\MainBundle\Entity\Feature $features
   * @return Place
   */
  public function addFeature(\Mv\MainBundle\Entity\Feature $features)
  {
    $this->features[] = $features;
    echo 'Add "'.$features.'" - Total '.count($this->features).'<br />';
    return $this;
  }

  /**
   * Remove features
   *
   * @param \Mv\MainBundle\Entity\Feature $features
   */
  public function removeFeature(\Mv\MainBundle\Entity\Feature $features)
  {
    $this->features->removeElement($features);
  }

  /**
   * Get features
   *
   * @return Doctrine\Common\Collections\Collection 
   */
  public function getFeatures()
  {
    return $this->features;
  }

  public function __construct()
  {
    $this->features = new \Doctrine\Common\Collections\ArrayCollection();
  }

特征实体:

namespace Mv\MainBundle\Entity;
…

/**
 * @ORM\Entity
 * @ORM\Table(name="feature")
 * @ORM\HasLifecycleCallbacks
 */
class Feature 
{
    use KrToolsTraits\PictureTrait;

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

    /**
     * @ORM\Column(name="label", type="string", length=255)
     * @Assert\NotBlank()
     */
    protected $label;

    /**
     * @ORM\ManyToOne(targetEntity="\Mv\MainBundle\Entity\FeatureCategory", inversedBy="features", cascade={"persist"})
     * @ORM\JoinColumn(name="category_id", referencedColumnName="id")
     */
    private $category;

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set label
     *
     * @param string $label
     * @return Feature
     */
    public function setLabel($label)
    {
        $this->label = $label;
        return $this;
    }

    /**
     * Get label
     *
     * @return string 
     */
    public function getLabel()
    {
        return $this->label;
    }

    /**
     * Set category
     *
     * @param Mv\MainBundle\Entity\FeatureCategory $category
     * @return Feature
     */
    public function setCategory(\Mv\MainBundle\Entity\FeatureCategory $category = null)
    {
        $this->category = $category;
        return $this;
    }

    /**
     * Get category
     *
     * @return Mv\MainBundle\Entity\FeatureCategory 
     */
    public function getCategory()
    {
        return $this->category;
    }
}

FeatureCategory实体:

namespace Mv\MainBundle\Entity;
...

/**
 * @ORM\Entity
 * @ORM\Table(name="feature_category")
 */
class FeatureCategory 
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(name="code", type="string", length=255)
     * @Assert\NotBlank()
     */
    protected $code;

    /**
     * @ORM\Column(name="label", type="string", length=255)
     * @Assert\NotBlank()
     */
    protected $label;

    /**
     * @ORM\OneToMany(targetEntity="\Mv\MainBundle\Entity\Feature", mappedBy="category", cascade={"persist", "remove"}, orphanRemoval=true)
     * @Assert\Valid()
     */
    private $features;

    public function __construct()
    {
       $this->features = new \Doctrine\Common\Collections\ArrayCollection();
    }

    /**
     * Get id
     *
     * @return integer 
     */
    public function getId()
    {
        return $this->id;
    }

    /**
     * Set code
     *
     * @param string $code
     * @return Feature
     */
    public function setCode($code)
    {
        $this->code = $code;
        return $this;
    }

    /**
     * Get code
     *
     * @return string 
     */
    public function getCode()
    {
        return $this->code;
    }

    /**
     * Set label
     *
     * @param string $label
     * @return Feature
     */
    public function setLabel($label)
    {
        $this->label = $label;
        return $this;
    }

    /**
     * Get label
     *
     * @return string 
     */
    public function getLabel()
    {
        return $this->label;
    }

    /**
    * Add features
    *
    * @param \Mv\MainBundle\Entity\Feature $features
    */
    public function addFeatures(\Mv\MainBundle\Entity\Feature $features){
      $features->setCategory($this);
      $this->features[] = $features;
    }

    /**
     * Get features
     *
     * @return Doctrine\Common\Collections\Collection 
     */
    public function getFeatures()
    {
        return $this->features;
    }

    /*
     * Set features
     */
    public function setFeatures(\Doctrine\Common\Collections\Collection $features)
    {
      foreach ($features as $feature)
      {
        $feature->setCategory($this);
      }
      $this->features = $features;
    }

    /**
     * Remove features
     *
     * @param Mv\MainBundle\Entity\Feature $features
     */
    public function removeFeature(\Mv\MainBundle\Entity\Feature $features)
    {
        $this->features->removeElement($features);
    }

    /**
     * Add features
     *
     * @param Mv\MainBundle\Entity\Feature $features
     * @return FeatureCategory
     */
    public function addFeature(\Mv\MainBundle\Entity\Feature $features)
    {
        $features->setCategory($this);
        $this->features[] = $features;
    }
}

功能表已填充,用户将无法添加功能,只能在表单集合中选择它们以将它们链接到地方。(Feature 实体目前仅与 Places 相关联,但稍后将与我的应用程序中的其他实体相关联,并将包含所有实体可用的所有功能)

在 Place 表单中,我需要显示 Place 可用功能的复选框,但我需要按类别分组显示它们。例子 :

访问(FeatureCategory - 代码 VIS):

  • 免费(功能)
  • 付费(功能)

使用的语言(FeatureCategory - 代码 LAN):

  • 英语(专题)
  • 法语(专题)
  • 西班牙语(特色)

我的想法

在我的 PlaceType 表单中使用虚拟表单,如下所示:

$builder
    ->add('name')
    ->add('visit', new FeatureType('VIS'), array(
        'data_class' => 'Mv\PlaceBundle\Entity\Place'
    ))
    ->add('language', new FeatureType('LAN'), array(
        'data_class' => 'Mv\PlaceBundle\Entity\Place'
    ));

并创建一个 FeatureType 虚拟表单,如下所示:

    class FeatureType extends AbstractType
    {
        protected $codeCat;

        public function __construct($codeCat)
        {
          $this->codeCat = $codeCat;
        }

        public function buildForm(FormBuilderInterface $builder, array $options)
        {
            $builder
                ->add('features', 'entity', array(
                    'class' => 'MvMainBundle:Feature',
                    'query_builder' => function(EntityRepository $er)
                    {
                      return $er->createQueryBuilder('f')
                              ->leftJoin('f.category', 'c')
                              ->andWhere('c.code = :codeCat')
                              ->setParameter('codeCat', $this->codeCat)
                              ->orderBy('f.position', 'ASC');
                    },
                    'expanded' => true,
                    'multiple' => true
                ));
        }

        public function setDefaultOptions(OptionsResolverInterface $resolver)
        {
            $resolver->setDefaults(array(
                'virtual' => true
            ));
        }

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

有了这个解决方案,我得到了我想要的,但绑定过程并没有保留所有的功能。它没有对它们进行分组,而是只保留我并坚持最后一组“语言”,并删除所有以前的特征数据。要查看它的实际效果,如果我选中 5 个复选框,它会很好地进入 Place->addFeature() 函数 5 次,但特征 arrayCollection 的长度依次为:1、2、1、2、3。

关于如何以另一种方式做到这一点的任何想法?如果我需要更改模型,我仍然可以做到。什么是最好的方法,可以在我未来也与功能相关的其他实体上重用,来处理这个?

感谢你们。

4

1 回答 1

0

我认为您最初的需求只是模板

因此,您不应调整表单和实体持久性逻辑来获得所需的自动生成表单。

你应该回到一个基本的形式

$builder
    ->add('name')
    ->add('features', 'entity', array(
        'class' => 'MvMainBundle:Feature',
        'query_builder' => function(EntityRepository $er) {
              return $er->createQueryBuilder('f')
              //order by category.xxx, f.position
            },
                'expanded' => true,
                'multiple' => true
            ));

并调整你的 form.html.twig

于 2013-03-13T22:22:50.487 回答