从SQL查询结果构建父子结构菜单数组的问题排查与优化
解决PHP构建MySQL父子菜单的两个问题:多余字段与键名错误
看起来你遇到了菜单构建的两个典型问题:深层子节点携带多余字段,以及子项用数字索引而非MNU_FUNC作为键。我来帮你排查代码问题并给出完整的替代实现方案。
问题根源分析
从你描述的错误来看,原代码大概率存在两个核心问题:
- 多余字段未过滤:递归构建子菜单时,直接将数据库查询得到的完整菜单项数组(包含
MNU_ID、MNU_PARENT等)传递下去,没有剥离不需要的字段,导致深层子节点保留了这些冗余数据。 - 键名未指定:添加子菜单时使用了
array_push或直接数组追加(如$parent['children'][] = $child),默认生成数字索引,而非用MNU_FUNC作为键名。
替代实现方案
下面是修正后的完整代码,包含数据库查询、递归构建菜单、过滤多余字段、指定键名的逻辑:
步骤1:数据库查询(获取权限范围内的所有菜单)
首先从MySQL查询用户权限覆盖的所有菜单,按父ID排序方便后续分组处理:
// 假设使用PDO连接数据库,可替换为MySQLi $pdo = new PDO('mysql:host=localhost;dbname=your_database', 'username', 'password'); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 替换为你的权限过滤逻辑,比如根据用户角色筛选菜单 $stmt = $pdo->prepare(" SELECT MNU_ID, MNU_NAME, MNU_FUNC, MNU_PARENT FROM your_menu_table WHERE /* 权限过滤条件,例如 MNU_ROLE IN (:user_roles) */ ORDER BY MNU_PARENT, MNU_ID "); // $stmt->bindParam(':user_roles', $currentUserRoles); // 按需绑定权限参数 $stmt->execute(); $menuItems = $stmt->fetchAll(PDO::FETCH_ASSOC);
步骤2:递归构建规范的菜单结构
核心逻辑是:
- 提前按
MNU_PARENT分组菜单项,提升递归效率 - 构建节点时只保留需要的展示字段,剥离冗余字段
- 子菜单以
MNU_FUNC作为键名存储,替代数字索引
function buildStructuredMenu(array $menuItems, int $parentId = 0): array { $menu = []; // 静态变量缓存分组后的菜单,避免递归重复遍历 static $groupedItems; if (!isset($groupedItems)) { $groupedItems = []; foreach ($menuItems as $item) { $groupedItems[$item['MNU_PARENT']][] = $item; } } // 处理当前父节点下的所有子项 foreach ($groupedItems[$parentId] ?? [] as $item) { // 构建当前节点的基础结构,只保留需要的字段 $currentNode = [ 'name' => $item['MNU_NAME'], // 可根据需求调整字段名 ]; // 递归构建子菜单 $children = buildStructuredMenu($menuItems, $item['MNU_ID']); if (!empty($children)) { $currentNode['children'] = $children; } // 用MNU_FUNC作为键名存入菜单 $menu[$item['MNU_FUNC']] = $currentNode; } return $menu; } // 生成根节点菜单(MNU_PARENT=0) $finalMenu = buildStructuredMenu($menuItems); // 格式化输出查看结果 echo json_encode($finalMenu, JSON_PRETTY_PRINT);
效果对比
原错误输出(示例)
{ "MNLS": { "MNU_ID": 1, "MNU_NAME": "菜单管理", "MNU_FUNC": "MNLS", "MNU_PARENT": 0, "children": [ { "MNU_ID": 2, "MNU_NAME": "安装管理", "MNU_FUNC": "INSTL", "MNU_PARENT": 1, "children": [] } ] } }
修正后预期输出
{ "MNLS": { "name": "菜单管理", "children": { "INSTL": { "name": "安装管理" } } } }
关键修正点说明
- 字段过滤:在构建
$currentNode时,仅保留需要展示的字段(如name),彻底移除MNU_ID、MNU_PARENT等冗余数据。 - 键名指定:通过
$menu[$item['MNU_FUNC']] = $currentNode将子菜单以MNU_FUNC为键存储,替代默认的数字索引。 - 性能优化:提前按
MNU_PARENT分组菜单项,避免递归时重复遍历整个数组,大幅提升构建效率。
内容的提问来源于stack exchange,提问作者Power Engineering
相关产品推荐
相关产品推荐

