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

PHP递归构建JSON树形结构子元素重复绑定父元素的解决方法

解决PHP递归构建树形结构时子元素重复绑定问题

问题根源

你遇到的重复绑定问题,核心错误是用level字段判断父子关系,而不是用明确的父节点关联ID(比如parent_id)。level仅表示元素的层级深度,相同层级的元素可能属于不同的父节点,只靠level匹配会把所有同层级元素挂到每个上层节点下,导致重复。

修改方案

前提:确保数据有明确的父关联字段

首先,你的扁平JSON数据需要包含parent_id字段(或类似字段),用来明确每个元素归属的父节点ID。比如原始数据格式应该是:

$flatData = [
    ['id' => 1, 'level' => 1, 'name' => '父元素1', 'parent_id' => 0],
    ['id' => 2, 'level' => 1, 'name' => '父元素2', 'parent_id' => 0],
    ['id' => 3, 'level' => 2, 'name' => '子元素1', 'parent_id' => 1],
    ['id' => 4, 'level' => 2, 'name' => '子元素2', 'parent_id' => 2],
];

修正递归函数

修改递归逻辑,用parent_id匹配父子关系,而不是level:

function buildTree($data, $parentId = 0) {
    $tree = [];
    foreach ($data as $item) {
        // 只筛选父ID等于当前传入值的元素
        if ($item['parent_id'] == $parentId) {
            // 递归查找该元素的子节点(父ID为当前元素ID)
            $children = buildTree($data, $item['id']);
            if (!empty($children)) {
                $item['children'] = $children;
            }
            $tree[] = $item;
        }
    }
    return $tree;
}

// 调用示例
$tree = buildTree($flatData);
print_r(json_encode($tree, JSON_PRETTY_PRINT));

如果数据没有parent_id的预处理方案

如果原始数据只有level和id,可以先通过层级顺序推断父节点,给每个元素添加parent_id:

$processedData = [];
$levelStack = [];

foreach ($flatData as $item) {
    $currentLevel = $item['level'];
    // 维护层级栈:弹出栈中层级>=当前层级的元素,栈顶即为父节点
    while (!empty($levelStack) && end($levelStack)['level'] >= $currentLevel) {
        array_pop($levelStack);
    }
    // 根节点父ID设为0,其他取栈顶元素的ID
    $item['parent_id'] = empty($levelStack) ? 0 : end($levelStack)['id'];
    $levelStack[] = $item;
    $processedData[] = $item;
}

// 再用修正后的buildTree处理
$tree = buildTree($processedData);

大数据量优化方案

如果数据量较大,每次递归遍历全量数据效率低,可以先按parent_id分组,减少遍历次数:

function buildTreeOptimized($data) {
    // 先按parent_id分组,构建映射表
    $parentMap = [];
    foreach ($data as $item) {
        $parentId = $item['parent_id'] ?? 0;
        $parentMap[$parentId][] = $item;
    }

    // 闭包递归构建树形结构
    $build = function($parentId) use (&$build, &$parentMap) {
        $tree = [];
        foreach ($parentMap[$parentId] ?? [] as $item) {
            $children = $build($item['id']);
            if (!empty($children)) {
                $item['children'] = $children;
            }
            $tree[] = $item;
        }
        return $tree;
    };

    return $build(0);
}

正确输出示例

执行修正后的代码,每个子元素只会归属到对应的父节点:

[
  {
    "id": 1,
    "level": 1,
    "name": "父元素1",
    "parent_id": 0,
    "children": [
      {
        "id": 3,
        "level": 2,
        "name": "子元素1",
        "parent_id": 1
      }
    ]
  },
  {
    "id": 2,
    "level": 1,
    "name": "父元素2",
    "parent_id": 0,
    "children": [
      {
        "id": 4,
        "level": 2,
        "name": "子元素2",
        "parent_id": 2
      }
    ]
  }
]

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.16 10:42:44