【问题标题】:Symfony Form render with Self Referenced Entity使用自引用实体渲染 Symfony 表单
【发布时间】:2014-06-12 14:46:50
【问题描述】:

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

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');
        },
    ));
}

实际上,当我呈现表单字段类别时,这类似于

  • Cat1
  • 二类
  • Cat3
  • 子目录1
  • 子类2
  • Cat4

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

  • Cat1
  • 二类
  • Cat3
  • --子目录1
  • --子类2
  • Cat4

问候。

【问题讨论】:

  • 你能分享你的渲染代码吗?
  • 我想说你应该使用Tree Doctrine Extension。因为它避免了嵌套实体的麻烦。但是,我不确定它会解决您的问题:/

标签: forms symfony self-reference


【解决方案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)
        },
    ));
}

注意:这是一项快速而肮脏的工作,因此您可能需要更正拼写错误、注释等。但是,您有这个想法!希望对您有所帮助。

【讨论】:

    【解决方案2】:

    这充其量只是一个很远的目标,但我认为它可以很容易地实现。

    在您的query_builder 中,您指定了'property' => 'name'。您需要将其更改为 'treeName'。 Doctrine 将尝试查找并调用属性的 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 次,这可能会对性能造成严重影响。

    你怎么看?项目的平均深度是多少?

    需要明确的是,name 属性及其 getter 和 setter 将保持不变。

    【讨论】:

    • 这并不妨碍您从示例中显示 Cat1、Cat2、SubCatOf2、SubcatOf3、Cat3。您不能确定子猫会在其父类别之后显示;)
    • 确实如此。让我想想我是否能想出类似的东西:)
    猜你喜欢
    • 1970-01-01
    • 2016-09-27
    • 1970-01-01
    • 2016-09-26
    • 2015-08-04
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 2014-06-08
    相关资源
    最近更新 更多