我有一个表示树的模型,如下所示:
class Category
{
//the definition of the $id and $parent_id is abbreviated
/**
* @ORM\OneToMany(targetEntity="Category", mappedBy="parent")
*/
private $children;
/**
* @ORM\ManyToOne(targetEntity="Category", inversedBy="children")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id")
*/
private $parent = null;
public function __construct()
{
$this->children = new ArrayCollection();
}
public function getChildren()
{
return $this->children;
}
//Getter and Setters following
}
为了检索生成的树,我创建了一个简单的\RecursiveIterator
,每次请求任何类别的数据时, BUT 都会创建一个数据库请求。我搜索了网页并查看了this文章,其中描述了在学说中创建分层数据。
如果在Symfony中无法使用这种方式,则可以在单个查询中加载数据库中的所有条目,并构造所有对象一次,而不是构建树。
所以我的问题是:如何在Symfony中使用Doctrine Hierarchical Data?如果不可能,如何在一个查询中加载数据库中的所有行?
向前谢谢!
答案 0 :(得分:0)
在Category
实体:
制作children
属性EXTRA_LAZY
,以防您访问它们。这将停止某些方法的完全加载触发,具体为count
,以防万一。
如果有孩子,请添加方法hasChildren
以返回true
。如果你只是count($this->children)
,它只会触发count
查询,因为EXTRA_LAZY
仍然比满载更好。
但还有另一种方式。在运行时,children
是PersistentCollection
的实例,它有一个方法getSnapshot
,它返回集合中元素的最后一个快照。换句话说,只加载了什么,甚至不会发出count
查询。这可能看起来有点 hack-ish 但它确实有效。
class Category
{
// ...
/**
* @ORM\OneToMany(targetEntity="Category", mappedBy="parent", fetch="EXTRA_LAZY")
*/
private $children;
// ...
public function hasChildren()
{
return count($this->children->getSnapshot()) > 0;
}
}
现在加载所有需要的实体/行,在其中创建方法findByRootId
CategoryRepository
。即使您将$level
设置为大于数据库中实际级别的值,这仍然有效。
由于额外的连接,性能会受到每个添加级别的影响,但除非您的树包含数千个元素,否则它的整体效果非常好。
public function findByRootId($id, $level = 10)
{
$children = '';
$joins = '';
for ($j = 0, $i = 1; $i <= $level; $i++, $j++) {
$children .= ", c{$i}";
$joins .= "LEFT JOIN c{$j}.children c{$i} ";
}
// Change `YourBundleName` with you actual namespace/bundle name
$query = "SELECT c0 {$children}
FROM YourBundleName:Category c0
{$joins}
WHERE c0.id = :rootId";
return $this
->getEntityManager()
->createQuery($query)
->setParameter('rootId', $id)
->getSingleResult();
}
最后在渲染时,如果您尝试访问未加载的子级,则会触发完整加载事件。使用hasChildren
方法查看孩子是否已加载。
注意:由于指定的level
,子项可能存在且未加载。
{% macro tree(parent) %}
<ul>
<li>
{{ parent.name }}
{% if parent.hasChildren %}
{% for child in parent.children %}
{{ _self.tree(child) }}
{% endfor %}
{% endif %}
</li>
</ul>
{% endmacro %}
{% import _self as builder %}
{% block body %}
{{ builder.tree(parent) }}
{% endblock %}
希望这会有所帮助,它不是很漂亮但它有效。