
本文详解如何在 laravel 中通过 eloquent 关联查询,将分类(category)及其关联的页面(pages)组织为带 children 嵌套数组的层级结构,避免扁平化拼接错误。
本文详解如何在 laravel 中通过 eloquent 关联查询,将分类(category)及其关联的页面(pages)组织为带 children 嵌套数组的层级结构,避免扁平化拼接错误。
在 Laravel 开发中,常需将一对多关系(如 Category → Page)转换为前端友好的树形数据结构。原始代码的问题在于:它将每个 category 和其 pages 分别 push 到同一级 $data 数组中,导致结果为线性扁平列表,而非“分类含子项”的嵌套格式。
正确的做法是——每个分类对应一个独立数组项,其 children 键下统一承载所属所有页面。以下是优化后的实现:
public function getCategory($lang): array
{
$data = [];
$categories = Category::where('language', $lang)->with('pages')->get();
foreach ($categories as $category) {
$item = [
'title' => $category->title,
'path' => $category->slug,
];
// 仅当存在关联页面时,才添加 children 字段
if ($category->pages->isNotEmpty()) {
$item['children'] = $category->pages->map(function ($page) {
return [
'title' => $page->title,
'path' => $page->slug,
];
})->values()->toArray(); // ->values() 重置键名,->toArray() 转为纯索引数组
}
$data[] = $item;
}
return $data;
}✅ 关键改进说明:
- 使用 $category->pages(Eloquent Collection)替代 $category['pages'],更安全且支持链式方法;
- ->map() 替代手动 foreach,语义清晰、代码简洁;
- ->values()->toArray() 确保 children 是连续数字索引数组(避免因模型懒加载或缓存导致的稀疏键);
- ->isNotEmpty() 比 !empty(...) 更符合 Laravel 风格,且对空集合判断更准确;
- 每个分类只生成一个顶层数组元素,children 作为其子键存在,结构严格符合预期 JSON 层级。
? 注意事项:
- 若 pages 关系未使用 with('pages') 预加载,循环中访问 $category->pages 将触发 N+1 查询,务必保持 eager loading;
- 如需支持多层嵌套(如页面再关联子模块),建议改用递归资源(Resource)或自定义 Collection 宏;
- 前端消费该结构时,可直接遍历 data,对含 children 的项做二级展开渲染。
此方案兼顾可读性、性能与 Laravel 最佳实践,是构建导航菜单、站点地图等层级数据的标准处理模式。

















