
本文详解如何使用递归函数在具有 `child` 键的多层嵌套数组(如菜单树)中精准定位目标节点,并完整返回该节点及其所有后代子节点。
在处理树状数据结构(例如导航菜单、分类目录或组织架构)时,常见需求是:根据某个唯一标识(如 slug)查找到对应节点,并同时获取其全部子树(即该节点自身 + 所有深层嵌套的 child 内容)。此时,简单的循环遍历无法覆盖任意深度的嵌套,必须借助递归。
你原始代码的核心问题在于:递归调用后未正确处理返回值。具体来说:
- 当在子树中找到匹配项时,getSlugData($slug, $row['child']) 确实会返回目标节点;
- 但你注释掉了 return 语句,导致该返回值被丢弃;
- 循环继续执行,最终函数默认返回空数组 []。
此外,原始逻辑仅检查顶层 $data 的每个元素,而未对任意层级的子数组(包括 child 下的多维关联数组)做统一递归探测——尤其当 child 是以数字键(如 "2"、"4")索引的关联数组时,需确保递归能穿透所有可能的嵌套路径。
✅ 正确做法是:
- 对当前层级每个 $row,先判断是否匹配目标 slug;
- 若不匹配,且 $row 是数组(含 child 或其他嵌套结构),则递归进入该数组;
- 关键一步:捕获递归调用的返回值,一旦非空,立即 return,避免被后续迭代覆盖;
- 使用 is_array() 统一判断可递归对象,而非硬编码 isset($row['child']),增强健壮性(适配不同字段名或混合结构)。
以下是优化后的完整实现:
<?php
$tree = '[{"id":1,"structure":1,"parent":0,"slug":"medicinskaya-ge","child":{"2":{"id":2,"structure":1.1,"parent":1,"slug":"dnk-diagnostika","child":{"3":{"id":3,"structure":"1.1.1","parent":2,"slug":"dnk-diagnostika","datafile":"ssz","template":"ssz"},"4":{"id":4,"structure":"1.1.2","parent":2,"slug":"dnk-diagnostika","child":{"5":{"id":5,"structure":"1.1.2.1","parent":4,"slug":"dnk-diagnostika"},"6":{"id":6,"structure":"1.1.2.2","parent":4,"slug":"testirovanie-ge"},"7":{"id":7,"structure":"1.1.2.3","parent":4,"slug":"dnk-diagnostika"}}},"8":{"id":8,"structure":"1.1.3","parent":2,"slug":"dnk-diagnostika"},"9":{"id":9,"structure":"1.1.4","parent":2,"slug":"texnologiya-kol"}}}}}]';
$tree = json_decode($tree, true);
function getSlugData(string $slug, array $data, string $key = 'slug'): array
{
foreach ($data as $row) {
// ✅ 优先检查当前节点是否匹配
if (isset($row[$key]) && $row[$key] === $slug) {
return $row;
}
// ✅ 若当前元素是数组(可能是 child、或其他嵌套结构),递归深入
if (is_array($row)) {
$found = getSlugData($slug, $row, $key);
if (!empty($found)) { // 非空表示已找到
return $found;
}
}
}
return []; // 未找到时返回空数组
}
// 示例调用
var_dump(getSlugData('testirovanie-ge', $tree));
// 输出包含 id=6 的节点及其完整上下文(注意:它本身无 child,故只返回自身)
var_dump(getSlugData('dnk-diagnostika', $tree));
// 将匹配第一个出现的 dnk-diagnostika(id=2),并返回其整个 child 子树
var_dump(getSlugData('not-exists', $tree));
// 返回 []
?>? 注意事项与最佳实践:
- 避免无限递归:确保输入数据结构为有限深度树(无环引用),PHP 默认递归限制为 100 层,超限将抛出 Fatal error;
- 性能提示:对超大型树(>10,000 节点),可考虑预构建 slug → node 的哈希映射表,实现 O(1) 查找;
- 字段灵活性:通过 $key 参数支持按 id、title 等其他字段搜索,提升函数复用性;
- 空值安全:使用 isset() + === 替代 ==,防止 '0' == 0 类型误判;
- 返回值约定:始终返回数组(匹配时为完整节点,未匹配时为 []),便于调用方统一判断(如 if (!empty($result)))。
掌握这种「深度优先 + 提前终止」的递归模式,即可高效处理任意深度的树形数据检索任务。










