Symfony表单使用自引用实体进行渲染

时间:2014-06-12 14:46:51

标签: forms symfony self-reference

我有一个包含自引用映射的实体。

class Category
{

    /**
     * @var integer
     *
     * @ORM\Column(name="id", type="integer")
     * @ORM\Id
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    private $id;

    /**
     * @var string
     *
     * @ORM\Column(name="name", type="string", length=100)
     */
    private $name;

    /**
     * @ORM\OneToMany(targetEntity="Category", mappedBy="parent")
     */
    private $children;

    /**
     * @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
     * @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
     */
    private $parent;

}

在我的CategoryType中,我有这个:

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $plan = $this->plan;

    $builder->add('name');

    $builder->add('parent', 'entity', array(
        'class' => 'xxxBundle:Category',
        'property' => 'name',
        'empty_value' => 'Choose a parent category',
        'required' => false,
        'query_builder' => function(EntityRepository $er) use ($plan) {
            return $er->createQueryBuilder('u')
                ->where('u.plan = :plan')
                ->setParameter('plan', $plan)
                ->orderBy('u.id', 'ASC');
        },
    ));
}

实际上,当我渲染表单字段Category时,这就像

  • CAT1
  • CAT2
  • 的Cat3
  • Subcat1
  • Subcat2
  • 3,4,5

我想知道它是否可能以及如何显示更像是一种简单的树形象:

  • CAT1
  • CAT2
  • 的Cat3
  • - Subcat1
  • - Subcat2
  • 3,4,5

问候。

2 个答案:

答案 0 :(得分:1)

我从你和jperovic所写的内容中得出了一些看似正确的东西。您的Category课程需要两个新属性:

  • $level将包含其父母的ID,例如" idA-idB"等等。此属性将用于在查询数据库时对结果进行排序,以便您可以确定SubCatOf3不会出现在Cat3之前!
  • $treeName将包含jperovic已写入的内容,并将在表单中打印。

我还使用了Doctrine Events [doc],因此当您更新/保留它们时,您不必担心这些属性的价值。

这是您全新的Category.php文件:

/**
* @ORM\HasLifeCycleCallbacks()
*/
class Category 
{

    private $level;

    private $treeName;

    /** 
    * Renders something like : "---- Subcategory A" 
    * @ORM\PreUpdate() 
    * @ORM\PrePersist()
    **/
    public function updateTreeName()
    {
       $itemDepth = 0;
       $parent = $this->parent;
       while ($parent != null) {
           $itemDepth++;
           $parent = $parent->getParent();
       }

       $this->treeName = str_repeat('--', $itemDepth) . ' ' . $this->name
    }

    /** renders something like : "idParent-idChild1-idChild2" 
    * @ORM\PreUpdate() 
    * @ORM\PrePersist()
    **/
    public function updateLevelName()
    {
       $this->level = '';
       $parent = $this->parent;
       while ($parent != null) {
           $parent = $parent->getParent();
           $this->level .= '-' . $p->getId();
       }
     }

    public function getTreeName()
    {
       return $this->treeName;
    }

    public function getLevel()
    {
        return $this->level; 
    }

    // ...
}

然后,将 query_builder 放在CategoryRepository.php中,如下所示:

namespace Foo\BarBundle\Entity;
use Doctrine\ORM\EntityRepository;

class CategoryRepository extends EntityRepository 
{
    public function getHierarchicalCategoryList($plan)
    {
        $qb = $this->createQueryBuilder('u')
           ->where('u.plan = :plan')
           ->setParameter('plan', $plan)
           ->orderBy('u.level', 'ASC');

        $return $qb;
    }
}

在您的CategoryType.php

public function buildForm(FormBuilderInterface $builder, array $options)
{
    $plan = $this->plan;

    $builder->add('name');

    $builder->add('parent', 'entity', array(
        'class' => 'xxxBundle:Category',
        'property' => 'treeName',
        'empty_value' => 'Choose a parent category',
        'required' => false,
        'query_builder' => function(EntityRepository $er) use ($plan) {
            return $er->getHierarchicalCategoryList($plan)
        },
    ));
}

注意:这是快速和肮脏的工作,因此您可能需要更正错别字,注释等。但是,您有这个想法!希望它有所帮助。

答案 1 :(得分:0)

这是一个非常长的镜头,但我认为它很容易实现。

在您指定query_builder的{​​{1}}内。您需要将其更改为'property' => 'name'。 Doctrine将尝试查找和调用属性的'treeName'方法 - 所有打印逻辑都在其中:

getter

由于需要对class Category { .... Everything else .... public function getTreeName(){ $itemDepth = 0; $p = $this->parent; while ( $p != null ){ $itemDepth++; $p = $p->getParent(); } return str_repeat('--', $itemDepth) . ' ' . $this->name } } 次的每个项目进行迭代,这可能会造成严重的性能损失。

你怎么看?物品的平均深度是多少?

为了清楚起见,depth属性及其getter和setter将保持不变。