You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Laravel中递归遍历同一模型的嵌套集树形节点?

搞定Laravel中嵌套集树形结构的递归遍历问题

嘿,我来帮你解决这个折腾了好几天的树形遍历问题!首先得先揪出你模型关联里的坑——你在parent()方法里加的where('parent_id',0)完全是画蛇添足,甚至直接搞崩了关联逻辑!

第一步:修复模型关联

你的parent()关联本来应该通过parent_id关联到任意父节点,而不是只限定父节点是顶级的。正确的关联代码应该是这样:

namespace App;

use Illuminate\Database\Eloquent\Model;

class ContextMenuItem extends Model
{
    // 关联父节点
    public function parent()
    {
        return $this->belongsTo(ContextMenuItem::class, 'parent_id');
    }

    // 关联子节点
    public function children()
    {
        return $this->hasMany(ContextMenuItem::class, 'parent_id');
    }

    // 可选:递归加载所有子孙节点的关联
    public function allChildren()
    {
        return $this->children()->with('allChildren');
    }
}

第二步:递归遍历的几种常用方法

方法1:递归加载所有层级的节点

如果你需要一次性获取顶级节点及其所有子孙,可以用Laravel的递归预加载:

// 获取所有顶级节点(parent_id=0)并加载所有层级的子孙
$topLevelMenuItems = ContextMenuItem::where('parent_id', 0)
    ->with('allChildren')
    ->get();

方法2:生成树形结构数组

如果要把数据转换成嵌套的数组结构,可以在模型里加一个递归方法:

// 在ContextMenuItem模型中添加
public function toTreeArray()
{
    $data = $this->only(['id', 'text', 'parent_id']); // 按需选择字段
    if ($this->children->isNotEmpty()) {
        $data['children'] = $this->children->map(function ($child) {
            return $child->toTreeArray();
        })->toArray();
    }
    return $data;
}

然后调用生成树形数组:

$treeArray = $topLevelMenuItems->map(function ($item) {
    return $item->toTreeArray();
})->toArray();

方法3:在视图中递归渲染(Blade组件)

如果要在前端渲染树形菜单,可以用Blade递归组件,更优雅:

  1. 创建组件文件 resources/views/components/menu-item.blade.php:
<li>
    {{ $item->text }}
    @if($item->children->isNotEmpty())
        <ul class="child-menu">
            @foreach($item->children as $child)
                <x-menu-item :item="$child" />
            @endforeach
        </ul>
    @endif
</li>
  1. 在父视图中使用:
<ul class="top-menu">
    @foreach($topLevelMenuItems as $item)
        <x-menu-item :item="$item" />
    @endforeach
</ul>

为什么你原来的代码无法工作?

你在parent()关联里加了->where('parent_id',0),这会导致:

  • 顶级节点(parent_id=0)的parent()方法会尝试找parent_id=0的父节点,显然不存在;
  • 子节点(比如id=2,parent_id=1)的parent()方法会过滤掉parent_id=1的节点,因为where条件要求父节点的parent_id必须是0,所以根本找不到父节点,整个关联链条就断了。

去掉这个多余的where条件,关联就能正常工作,后续的递归遍历也就没问题啦!

内容的提问来源于stack exchange,提问作者Egyptian Eagle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:25:59