如何用Laravel Eloquent实现递归查询生成树形菜单JSON?
Laravel Eloquent 实现自关联菜单树形结构
方法一:Eloquent关联+递归集合处理
适合层级较少的场景,通过预加载解决N+1查询问题,再递归格式化树形结构:
控制器代码实现
class MenuTC extends Controller { public function index() { // 获取根菜单,递归预加载所有层级子菜单 $rootMenus = Menu::whereNull('idmenu') ->with($this->getNestedRelations('menus', 5)) // 5为最大层级,按需调整 ->get(); // 格式化树形结构 $tree = $this->formatMenuTree($rootMenus); return response()->json($tree); } // 生成多层级预加载路径 private function getNestedRelations($relation, $depth) { $relations = []; $current = $relation; for ($i = 0; $i < $depth; $i++) { $relations[] = $current; $current .= '.' . $relation; } return $relations; } // 递归组装树形结构 private function formatMenuTree($menus) { return $menus->map(function ($menu) { $item = [ 'id' => $menu->id, 'name' => $menu->name, 'children' => [] ]; if ($menu->menus->isNotEmpty()) { $item['children'] = $this->formatMenuTree($menu->menus); } return $item; })->toArray(); } }
方法二:MySQL 8.0+递归CTE查询(性能优先)
如果你的MySQL版本是8.0及以上,用递归CTE一次性查询所有数据,再组装树形结构,适合层级深、数据量大的场景:
控制器代码实现
class MenuTC extends Controller { public function index() { // 递归CTE查询所有菜单,包含层级和路径 $menus = DB::select(" WITH RECURSIVE menu_tree AS ( SELECT id, idmenu, name, 0 AS level, CAST(id AS CHAR(255)) AS path FROM tb__menu WHERE idmenu IS NULL UNION ALL SELECT m.id, m.idmenu, m.name, mt.level + 1, CONCAT(mt.path, ',', m.id) FROM tb__menu m JOIN menu_tree mt ON m.idmenu = mt.id ) SELECT * FROM menu_tree ORDER BY path "); // 转换为集合并组装树形 $menuCollection = collect($menus); $tree = $this->buildTreeFromFlatList($menuCollection); return response()->json($tree); } // 从扁平化列表构建树形结构 private function buildTreeFromFlatList($menus) { $menuMap = []; $tree = []; // 先建立菜单ID到数据的映射 foreach ($menus as $menu) { $menuMap[$menu->id] = [ 'id' => $menu->id, 'name' => $menu->name, 'children' => [] ]; } // 将子菜单挂载到父菜单下 foreach ($menus as $menu) { if ($menu->idmenu !== null) { $menuMap[$menu->idmenu]['children'][] = $menuMap[$menu->id]; } else { $tree[] = $menuMap[$menu->id]; } } return $tree; } }
方法三:使用Laravel扩展包(快捷方案)
不想手动写递归逻辑的话,可以用成熟的嵌套集扩展包laravel-nestedset:
- 安装扩展包
composer require kalnoy/nestedset
- 修改Menu模型
use Kalnoy\Nestedset\NodeTrait; class Menu extends Model { use NodeTrait; protected $table = 'tb__menu'; // 保留原有fillable、casts等配置 }
- 控制器快速生成树形结构
class MenuTC extends Controller { public function index() { $tree = Menu::get()->toTree(); return response()->json($tree); } }
注:扩展包需要额外的
lft、rgt、depth字段,可通过扩展包提供的迁移命令生成。
内容的提问来源于stack exchange,提问作者pedroPG
相关产品推荐
相关产品推荐

