我有一个非常简单的实体(WpmMenu),它保存以自引用关系相互连接的菜单项(它被称为相邻列表)?所以在我的实体中,我有:
protected $id
protected $parent_id
protected $level
protected $name
对于所有的 getter/setter,关系是:
/**
* @ORM\OneToMany(targetEntity="WpmMenu", mappedBy="parent")
*/
protected $children;
/**
* @ORM\ManyToOne(targetEntity="WpmMenu", inversedBy="children", fetch="LAZY")
* @ORM\JoinColumn(name="parent_id", referencedColumnName="id", onUpdate="CASCADE", onDelete="CASCADE")
*/
protected $parent;
public function __construct() {
$this->children = new ArrayCollection();
}
一切正常。当我渲染菜单树时,我从存储库中获取根元素,获取它的子元素,然后遍历每个子元素,获取它的子元素并递归执行此操作,直到我渲染了每个项目。
会发生什么(以及我正在寻求解决方案)是这样的:目前我有 5 个 level=1 项目,每个项目都附加了 3 个 level=2 项目(将来我将使用 level=3 项目以及)。要获取我的菜单树 Doctrine 的所有元素,请执行以下操作:
- 1 查询根元素 +
- 1 次查询以获取根元素的 5 个子项(级别 = 1)+
- 5 个查询以获取每个级别 1 项目的 3 个子项(级别 = 2)+
- 15 个查询 (5x3) 以获取每个级别 2 项目的子项 (level=3)
总计:22 个查询
所以,我需要为此找到一个解决方案,理想情况下我只想有 1 个查询。
所以这就是我想要做的: 在我的实体存储库(WpmMenuRepository)中,我使用 queryBuilder 并获得按级别排序的所有菜单项的平面数组。获取根元素(WpmMenu)并从加载的元素数组中“手动”添加其子元素。然后对孩子递归地执行此操作。这样做我可以拥有相同的树,但只有一个查询。
所以这就是我所拥有的:
WpmMenuRepository:
public function setupTree() {
$qb = $this->createQueryBuilder("res");
/** @var Array */
$res = $qb->select("res")->orderBy('res.level', 'DESC')->addOrderBy('res.name','DESC')->getQuery()->getResult();
/** @var WpmMenu */
$treeRoot = array_pop($res);
$treeRoot->setupTreeFromFlatCollection($res);
return($treeRoot);
}
在我的 WpmMenu 实体中,我有:
function setupTreeFromFlatCollection(Array $flattenedDoctrineCollection){
//ADDING IMMEDIATE CHILDREN
for ($i=count($flattenedDoctrineCollection)-1 ; $i>=0; $i--) {
/** @var WpmMenu */
$docRec = $flattenedDoctrineCollection[$i];
if (($docRec->getLevel()-1) == $this->getLevel()) {
if ($docRec->getParentId() == $this->getId()) {
$docRec->setParent($this);
$this->addChild($docRec);
array_splice($flattenedDoctrineCollection, $i, 1);
}
}
}
//CALLING CHILDREN RECURSIVELY TO ADD REST
foreach ($this->children as &$child) {
if ($child->getLevel() > 0) {
if (count($flattenedDoctrineCollection) > 0) {
$flattenedDoctrineCollection = $child->setupTreeFromFlatCollection($flattenedDoctrineCollection);
} else {
break;
}
}
}
return($flattenedDoctrineCollection);
}
这就是发生的事情:
一切正常,但我最终每个菜单项都出现了两次。;) 现在我有 23 个查询,而不是 22 个查询。所以我实际上使情况恶化了。
我认为,真正发生的情况是,即使我添加了“手动”添加的子项,WpmMenu 实体也不会被视为与数据库同步,并且一旦我对其子项执行 foreach 循环,加载就会在 ORM 中触发加载和添加已经“手动”添加的相同子项。
问:有没有办法阻止/禁用此行为并告诉这些实体它们与数据库同步,因此不需要额外的查询?