Symfony 2 - 一个数据库行的多个表单字段

时间:2012-09-21 08:57:50

标签: symfony doctrine-orm symfony-forms symfony-2.1

我使用Symfony 2.1和Doctrine 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,但稍后将与我的应用程序中的其他实体相关联,并将包含所有实体可用的所有功能)

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

访问(FeatureCategory - 代码VIS):

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

语言(FeatureCategory - code 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';
        }
    }

使用这个解决方案,我得到了我想要的东西,但绑定过程并没有保留所有功能。它不是对它们进行分组,而是只保留我并坚持最后一组&#34;语言&#34;,并删除所有prevouses功能数据。要查看它的实际效果,如果我选中5个复选框,它会很好地进入Place-&gt; addFeature()函数5次,但是要素arrayCollection的长度是连续的:1,2,1,2,3。 / p>

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

谢谢你们。

1 个答案:

答案 0 :(得分: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