PHP递归构建分类树时如何正确传递父级面包屑至子节点
问题
通过查询获取所有食品分类数据,现有递归函数用于构建树形结构,另有递归函数将其扁平化并添加深度标识。递归逻辑本身运行正常,但在将父级面包屑传递给子节点时遇到问题——需要为每个层级(包括最终子节点)保留对应面包屑,然而当前函数会携带所有过往面包屑,甚至包含其他父节点的子节点面包屑。树形结构的其余部分符合父子关系,但面包屑却出现混乱。
查询结果示例
$QueryResults = array ( 0 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 42 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 1 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Alcoholic' ) ) ), 1 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 39 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 4 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Beef' ) ) ), 2 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 1 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => NULL ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Beverages' ) ) ), 3 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 44 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 1 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Carbonated' ) ) ), 4 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 46 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 1 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Coffee' ) ) ), 5 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 4 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => NULL ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Meats' ) ) ), 6 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 44 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 4 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Processed Meats' ) ) ), 7 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 45 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 44 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Luncheon Meats' ) ) ), 8 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 7 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 4 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Pork' ) ) ), 9 => array ( 'Fields' => array ( 0 => array ( 'FieldName' => 'id', 'Value' => 8 ), 1 => array ( 'FieldName' => 'parent_id', 'Value' => 4 ), 2 => array ( 'FieldName' => 'name', 'Value' => 'Poultry' ) ) ) );
尝试的代码
function buildTree($elements, $parentId, $crumbs) { $branch = array(); foreach ($elements as $element) { if ($element['Fields'][1]['Value'] == $parentId) { $currentCrumbs[] = array('Fields' => array(0 => array('FieldName' => 'id','Value' => $element['Fields'][0]['Value']),1 => array('FieldName' => 'Crumb','Value' => $element['Fields'][2]['Value']))); if (is_array($crumbs)) { // lower levels $combinedCrumbs = array_merge($crumbs, $currentCrumbs); } else { // top level $combinedCrumbs = $currentCrumbs; } $element['BreadCrumbs'] = $combinedCrumbs; $children = buildTree($elements, $element['Fields'][0]['Value'], $combinedCrumbs); if ($children) { $element['SubGroups'] = $children; } $branch[] = $element; } } return $branch; }
错误表现
- Carbonated节点的面包屑中混入了Alcoholic的条目
- Beverages的面包屑错误出现在Meats顶级节点中
- 预期每个节点仅保留自身层级链的面包屑
解决方案
问题原因
代码里的$currentCrumbs未在每次循环时重置,导致处理同级节点时,前一个节点的面包屑会被追加到后一个节点的currentCrumbs中,最终所有同级节点的面包屑互相污染。
修正后的代码
function buildTree($elements, $parentId, $crumbs = []) { $branch = []; foreach ($elements as $element) { if ($element['Fields'][1]['Value'] == $parentId) { // 每次处理节点时,创建独立的当前节点面包屑项 $currentCrumb = [ 'Fields' => [ ['FieldName' => 'id', 'Value' => $element['Fields'][0]['Value']], ['FieldName' => 'Crumb', 'Value' => $element['Fields'][2]['Value']] ] ]; // 合并父级面包屑与当前节点面包屑,生成完整路径链 $combinedCrumbs = array_merge($crumbs, [$currentCrumb]); $element['BreadCrumbs'] = $combinedCrumbs; // 递归处理子节点,传递当前节点的完整面包屑链 $children = buildTree($elements, $element['Fields'][0]['Value'], $combinedCrumbs); if (!empty($children)) { $element['SubGroups'] = $children; } $branch[] = $element; } } return $branch; }
关键改动说明
- 初始化默认参数:给
$crumbs设置默认空数组,调用时无需手动传空值。 - 独立当前节点面包屑:每次处理节点时创建单个的
$currentCrumb,避免同级节点的面包屑互相追加污染。 - 正确合并路径:用
array_merge($crumbs, [$currentCrumb])生成当前节点的完整面包屑链,确保仅包含自身及所有父节点的路径。
调用示例:
$tree = buildTree($QueryResults, null);
处理后每个节点的面包屑会是正确的层级链,例如:
- Beverages的面包屑仅包含自身
- Alcoholic的面包屑为
Beverages > Alcoholic - Luncheon Meats的面包屑为
Meats > Processed Meats > Luncheon Meats
内容的提问来源于stack exchange,提问作者FigureOfCode
相关产品推荐
相关产品推荐

