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

从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:递归构建规范的菜单结构

核心逻辑是:

  1. 提前按MNU_PARENT分组菜单项,提升递归效率
  2. 构建节点时只保留需要的展示字段,剥离冗余字段
  3. 子菜单以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": "安装管理"
      }
    }
  }
}

关键修正点说明

  1. 字段过滤:在构建$currentNode时,仅保留需要展示的字段(如name),彻底移除MNU_ID、MNU_PARENT等冗余数据。
  2. 键名指定:通过$menu[$item['MNU_FUNC']] = $currentNode将子菜单以MNU_FUNC为键存储,替代默认的数字索引。
  3. 性能优化:提前按MNU_PARENT分组菜单项,避免递归时重复遍历整个数组,大幅提升构建效率。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 08:25:05