我们有一个我认为不是不寻常的实体
Foo
用例,它可以有任意数量的父母和孩子Foo
s。这是通过一个 FooRelations
实体实现的,它有一个父实体 Foo
和一个子实体 Foo
。 Foo
具有名为 manyToOne
和 childFoos
的 parentFoos
关系,它们被设置为 EAGER
.
因为图表很复杂,尝试加载完整的
Foo
会导致数以千计的数据库查询/行程 - 加载Foo
(一个查询),加载所有Foo
的孩子(一个查询),为每个孩子Foo
,在两个方向加载它的所有子项(每个一个查询)等等。
图表并没有那么复杂或庞大,总共大约有 5000
Foo
s。 Foo
s 也很小,我们谈论的是整个表的总计几 KB 的数据。我们只想说“加载所有Foo
,然后加载FooRelations
并填充所有对象。相反,所有Foo
都有AbstractLazyCollection
作为getChildren
/getParent
,尽管EAGER
标志在访问之前不会被填充。
我们做错了什么吗?有没有办法完成我们在这里要做的事情?加载数据本身与
SELECT f, p, c FROM Foo::class LEFT JOIN f.parent p LEFT JOIN f.child c
类似,因此查询数据不是问题,只需将其保存在缓存中即可。
由于事先不知道层次结构的深度,你最终总会有一些重复出现的逻辑。您可以通过递归实现这一点这一事实意味着您可以通过 stack 实现您的结果,尽管如果您使用递归,那么您使用的堆栈是内存中堆叠函数调用的一部分,称为 stack .您可以将算法转换为迭代算法。因此,您可以显式使用堆栈并执行如下操作,而不是自调用函数:
//$stack is a properly initialized empty stack
//$tree is a properly built tree where your hierarchy is stored
$stackItems = getNodesByParentID(null);
foreach ($stackItems as $stackItem) $stack->push($stackItem);
while (!$stack->isEmpty()) {
$stackNode = $stack->pop();
//The following two lines appeal to find and add, methods that are
//assumed to be implemented, but they are less relevance to the point
//of the question, so I will assume you have no trouble implementing them
$parentNode = find($tree, $stackNode->parentID);
add($tree, $stackNode);
$stackItems = getNodesByParentID($stackNode->parentID);
foreach ($stackItems as $stackItem) $stack->push($stackItem);
}
本质上,您在算法上执行类似于递归的操作,而您使用的是堆内存而不是堆栈内存,并且您的算法是迭代的。
然而,您将需要考虑其他问题,例如图表中的循环或损坏的数据。为了简单起见,我暂时忽略了这些问题,但它们肯定是可以解决的。