【问题标题】:Recursive self join in LaravelLaravel中的递归自加入
【发布时间】:2020-11-25 08:32:01
【问题描述】:

我有下表:

| 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;
        

然而,上面的代码只返回第一级而忽略了更深层次的子级。

我在这里做错了什么?

编辑

事实上,我现在明白我实际上是在正确检索所有数据,只是我看到了输出数组的顶层。

问题是如何展平输出以显示单个数组中的数据?

编辑 2

所以我设法创建了一个函数,使用以下代码(例如在控制器中)将输出平面化为一级数组:

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);

【问题讨论】:

  • with 将执行一次select if exists。它不是递归操作。您可以查看对子集合执行一些逻辑以获取所有节点。

标签: laravel eloquent self-join


【解决方案1】:

您可以使用with() 执行eager loading。所以它看起来像

Tree::where('node_id', 1)->with('allchildren')->get();

您也可以使用嵌套的预加载。

Tree::where('node_id', 1)->with('children.children')->get();

【讨论】:

  • 谢谢,这确实有效,但是如何使输出变平?
  • @Lamar 我很高兴 :) 因为它返回一个 Eloquent 集合,所以您可以使用 collection methods 来进一步操作您的输出。有一个 flatten() 方法,但我不确定它是否会有所帮助,因为您的节点可能有很多子节点,因此根据您的预期输出,您可能必须使用 mappings 或其他 iterative 方法。
【解决方案2】:

在我的脑海中,我认为您可以通过这样的方式实现这一目标。

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 并创建一个新的节点集合。这种特定逻辑的缺点是它将获取单个级别集合中的所有节点。

【讨论】:

  • 其实我需要的是让所有的孩子都在一个级别。基本上类似于表结构。但是这条线是什么意思:$nodes = collect();
  • 那是打开一个新的空集合。你测试过这段代码吗?
  • 对不起,我是 Laravel 的新手。所以你的意思是我应该用这个方法替换我的关系,或者这是一个帮助方法来扁平化输出或我的关系?
  • 你可以用它来代替模型中的allchildren方法,或者把它作为一个新方法放到模型中,如果你对结果感到满意,你可以使用它。
  • 它不工作。不确定如何调用此方法,无论是作为关系属性、抽象方法还是应该将父节点作为参数传递?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-03-29
  • 2021-02-23
  • 2018-12-20
  • 2014-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多