Laravel 9.x 中用 Collection 构建无限级分类树的核心是 groupBy('parent_id') 分组后递归组装:先一次性查出全部分类,再按 parent_id 分组建立索引,最后通过闭包递归注入 children 字段,全程内存操作、无 N+1 查询。

在 Laravel 9.x 中,用集合(Collection)迭代构建无限级分类树,核心是利用 Collection::groupBy() 和递归映射,避免 N+1 查询,同时保持逻辑清晰、性能可控。关键不在于硬写递归函数,而是把数据“分层归组 + 自顶向下组装”。
准备带 parent_id 的扁平分类数据
确保数据库中分类表有 id、name、parent_id 字段(parent_id 为 0 或 null 表示根节点)。使用 Eloquent 一次性查出全部数据:
$categories = Category::select('id', 'name', 'parent_id', 'sort')->get();
这一步必须做——后续所有操作都基于这个完整集合,不再发起额外查询。
用 groupBy 按 parent_id 分组,建立父子索引
对集合调用 groupBy('parent_id'),得到一个以 parent_id 为键、子项集合为值的数组:
$grouped = $categories->groupBy('parent_id');
-
$grouped[0]是所有一级分类(根节点) -
$grouped[5]是 parent_id = 5 的所有子分类 - 未被任何分类作为 parent_id 引用的 id,在
$grouped中不会自动出现(无需初始化空数组)
递归组装树结构(无数据库查询)
定义一个闭包或私有方法,接收当前父 ID(如 0)和分组后的集合,返回该层级下的树形子集:
$buildTree = function ($parentId, $grouped) use (&$buildTree) {
return $grouped->get($parentId, collect())->map(function ($item) use ($buildTree, $grouped) {
return $item->merge([
'children' => $buildTree($item['id'], $grouped)
]);
});
};
$tree = $buildTree(0, $grouped); // 从根节点开始
说明:
-
collect()确保即使某 parent_id 无子项,get()也返回空集合,避免报错 -
merge()将children字段注入原模型/数组,保留原有属性(如 name、sort) - 全程不 new Model、不 query,纯内存操作,适合几百条以内分类
转成标准数组或支持 API 返回
如果需要 JSON 响应,直接调用 $tree->toArray() 即可;若需支持前端 Tree 组件(如 Element Plus),可加字段适配:
$tree = $buildTree(0, $grouped)->map(function ($node) {
return [
'value' => $node['id'],
'label' => $node['name'],
'children' => $node['children']->isEmpty() ? [] : $node['children']->toArray()
];
});
这样输出结构兼容多数前端树控件,且 children 为空时是 [] 而非 null,更健壮。


















