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

如何用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:

  1. 安装扩展包
composer require kalnoy/nestedset
  1. 修改Menu模型
use Kalnoy\Nestedset\NodeTrait;

class Menu extends Model
{
    use NodeTrait;

    protected $table = 'tb__menu';

    // 保留原有fillable、casts等配置
}
  1. 控制器快速生成树形结构
class MenuTC extends Controller
{
    public function index()
    {
        $tree = Menu::get()->toTree();
        return response()->json($tree);
    }
}

注:扩展包需要额外的lft、rgt、depth字段,可通过扩展包提供的迁移命令生成。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 19:31:13