递归自加入 Laravel
Recursive self join in Laravel
我有以下 table:
| node_id | node_name | parent_id |
| 1 | Node 1 | |
| 2 | Node 2 | 1 |
| 3 | Node 3 | 1 |
| 4 | Node 4 | 2 |
我想递归检索特定节点下的所有节点。
我的模型是这样的:
class Tree extends Model
{
public function parent()
{
return $this->belongsTo(self::class, 'node_id');
}
public function children()
{
return $this->hasMany(self::class, 'parent_id', 'node_id');
}
public function allchildren()
{
return $this->hasMany(self::class, 'parent_id', 'node_id')->with('allchildren');;
}
}
现在,如果我想检索一个节点的所有后代,我会执行以下操作:
$node = Tree::where('node_id', 1)->first();
$nodes = $node->allchildren;
但是上面的代码只返回第一层而忽略了更深层的 children。
我做错了什么?
编辑
事实上,我现在明白我实际上正在正确检索所有数据,只是我看到了输出数组的顶层。
问题是如何将输出展平以显示单个数组中的数据?
编辑 2
所以我设法创建了一个函数,使用以下代码(例如在控制器中)将输出扁平化为 one-level 数组:
public function flattenTree($array)
{
$result = [];
foreach ($array as $item) {
$result[] = ['node_id'=>$item['node_id'], 'node_name'=>$item['node_name']];
$result = array_merge($result, $this->flattenTree($item['allchildren']));
}
return array_filter($result);
}
然后这样称呼:
$flatTree = $this->flattenTree($nodes);
在我的脑海中,我认为你可以用这样的东西来实现。
public function allChildren()
{
$nodes = collect();
$this->children->each(function($node) use ($nodes){
$nodes->push($node);
$nodes = $nodes->merge($node->allChildren()); //merge this node's children
});
return $nodes;
}
逻辑基本上是为每个 children
调用 allChildren
并创建一个新的节点集合。这种特殊逻辑的缺点是它将所有节点都放在一个级别集合中。
您可以 eager loading 使用 with()
。所以它看起来像
Tree::where('node_id', 1)->with('allchildren')->get();
您也可以嵌套预加载。
Tree::where('node_id', 1)->with('children.children')->get();
我有以下 table:
| node_id | node_name | parent_id |
| 1 | Node 1 | |
| 2 | Node 2 | 1 |
| 3 | Node 3 | 1 |
| 4 | Node 4 | 2 |
我想递归检索特定节点下的所有节点。
我的模型是这样的:
class Tree extends Model
{
public function parent()
{
return $this->belongsTo(self::class, 'node_id');
}
public function children()
{
return $this->hasMany(self::class, 'parent_id', 'node_id');
}
public function allchildren()
{
return $this->hasMany(self::class, 'parent_id', 'node_id')->with('allchildren');;
}
}
现在,如果我想检索一个节点的所有后代,我会执行以下操作:
$node = Tree::where('node_id', 1)->first();
$nodes = $node->allchildren;
但是上面的代码只返回第一层而忽略了更深层的 children。
我做错了什么?
编辑
事实上,我现在明白我实际上正在正确检索所有数据,只是我看到了输出数组的顶层。
问题是如何将输出展平以显示单个数组中的数据?
编辑 2
所以我设法创建了一个函数,使用以下代码(例如在控制器中)将输出扁平化为 one-level 数组:
public function flattenTree($array)
{
$result = [];
foreach ($array as $item) {
$result[] = ['node_id'=>$item['node_id'], 'node_name'=>$item['node_name']];
$result = array_merge($result, $this->flattenTree($item['allchildren']));
}
return array_filter($result);
}
然后这样称呼:
$flatTree = $this->flattenTree($nodes);
在我的脑海中,我认为你可以用这样的东西来实现。
public function allChildren()
{
$nodes = collect();
$this->children->each(function($node) use ($nodes){
$nodes->push($node);
$nodes = $nodes->merge($node->allChildren()); //merge this node's children
});
return $nodes;
}
逻辑基本上是为每个 children
调用 allChildren
并创建一个新的节点集合。这种特殊逻辑的缺点是它将所有节点都放在一个级别集合中。
您可以 eager loading 使用 with()
。所以它看起来像
Tree::where('node_id', 1)->with('allchildren')->get();
您也可以嵌套预加载。
Tree::where('node_id', 1)->with('children.children')->get();