Laravel基于数据库构建无限级多级菜单:代码问题及优化求助
Laravel 多级菜单构建优化方案(支持无限级)
原代码存在的问题
- 二级菜单赋值错误:原代码中
$menu['subMenu'][$key]['subMenu'] = $subMenusTwoArr会覆盖之前的子菜单,且$subMenusTwoArr未正确追加子项,最终仅保留最后一个二级菜单。 - N+1查询性能问题:每获取一级菜单就发起一次DB查询,层级越多查询次数越多,严重拖慢性能。
- 硬编码层级限制:代码仅写死二级菜单逻辑,无法扩展到无限级菜单需求。
优化方案
1. 一次性获取所有菜单数据
通过单次DB查询拉取所有符合条件的菜单(含所有子层级),彻底解决N+1查询问题:
public function getMenu() { // 一次性获取所有关联top_menu的菜单(含所有子层级) $allMenus = DB::table('menu as m') ->select('m.id', 'm.title', 'm.click_type', 'm.click_destination', 'm.ordering', 'm.parent_menu_id') ->join('menu_location as ml', 'm.id', '=', 'ml.menu_id') ->join('itemmeta as im', 'ml.item_meta_id', '=', 'im.id') ->where('m.site_id', 100) ->where('m.tenant_id', 10) ->where('im.slug', 'top_menu') ->where('im.is_enabled', 1) ->orderBy('m.parent_menu_id') ->orderBy('m.ordering') ->get() ->toArray(); // 将菜单按parent_menu_id分组,方便快速查找子菜单 $groupedMenus = collect($allMenus)->groupBy('parent_menu_id')->toArray(); // 递归构建树形菜单,从parent_menu_id=0的根菜单开始 return $this->buildMenuTree(0, $groupedMenus); }
2. 递归构建无限级菜单树
编写递归函数,自动处理所有层级的子菜单,无需硬编码层级:
private function buildMenuTree($parentId, $groupedMenus) { $menuTree = []; // 当前父ID无对应子菜单时,直接返回空数组 if (!isset($groupedMenus[$parentId])) { return $menuTree; } // 遍历当前父ID下的所有子菜单 foreach ($groupedMenus[$parentId] as $menuItem) { $menu = [ 'id' => $menuItem->id, 'title' => $menuItem->title, 'click_type' => $menuItem->click_type, 'click_destination' => $menuItem->click_destination, 'ordering' => $menuItem->ordering ]; // 递归获取当前菜单的子菜单 $childMenus = $this->buildMenuTree($menuItem->id, $groupedMenus); if (!empty($childMenus)) { $menu['subMenu'] = $childMenus; } $menuTree[] = $menu; } return $menuTree; }
代码核心优势
- 性能优化:仅发起1次DB查询,避免多次查询带来的性能损耗。
- 无限级支持:递归函数自动适配所有层级的子菜单,无需修改代码即可扩展菜单深度。
- 有序展示:查询时通过
orderBy保证菜单按父ID和自定义排序规则展示。 - 易维护:逻辑集中,后续修改菜单字段或筛选条件只需调整一处代码。
内容的提问来源于stack exchange,提问作者Dhairyashil Nalawade
相关产品推荐
相关产品推荐

