跳转到主内容
极星编程网:以代码为星,赴技术山海!

如何从递归查询结果中提取扁平化的 ID 和名称列表

本文介绍在 laravel 中如何将嵌套的树形结构数据(如无限级分类)高效转换为扁平化的 id-name 映射列表,既支持 eloquent 原生查询优化,也提供递归遍历方案,兼顾性能与可读性。 本文介绍在 laravel 中如何将嵌套的树形结构数据(如无限级分类)高效转换为扁平化的 id-name 映射列表,既支持 eloquent 原生查询优化,也提供递归遍历方案,兼顾性能与可读性。 在构建树形菜单、组织架构或分类系统时,常通过 parent_id 实现无限级递归关系,并使用 with('children') 预加载形成嵌套集合。但业务中往往还需一份 扁平化(flat)的元数据列表 ——例如用于下拉选择、搜索建议或前端快速映射,此时仅需每个节点的 id 和 name,无需层级结构。 ✅ 最优解:直接数据库查询(推荐) 若只需 id 和 name, 完全无需加载树形结构再递归处理 。Eloquent 提供最简洁高效的方案:
$result = Hierarchy::select('id', 'name')->get()->toArray(); // 输出示例: // [ // ["id" => 1, "name" => "parent 1"], // ["id" => 2, "name" => " parent 2"], // ["id" => 21, "name" => "child A of 2"], // ... // ]
✅ 优势: 单次 SQL 查询,零 PHP 递归开销; 自动包含所有层级节点(因 select() 作用于全表); 兼容任意深度,无栈溢出风险; 可链式添加 orderBy、where 等条件。 ⚠️ 注意:此方法适用于「所有节点均在同一张表」且逻辑上允许全量读取的场景。若需按树根过滤(如仅某几棵子树),仍需结合 whereNull('parent_id') 的递归查询,但扁平化步骤应移至 PHP 层。 ? 备选方案:PHP 递归扁平化(适配已有嵌套结构) 当已存在 $hierarchicalData(如 with('children') 加载后的集合),需从中提取扁平列表时,可使用以下无副作用的递归函数:
public function flattenNodes($nodes): array { $result = []; foreach ($nodes as $node) { // 当前节点基础字段 $result[] = ['id' => $node->id, 'name' => $node->name]; // 递归处理子节点(自动处理无限深度) if ($node->relationLoaded('children') && $node->children->isNotEmpty()) { $result = array_merge($result, $this->flattenNodes($node->children)); } } return $result; } // 使用示例 $hierarchical = Hierarchy::whereNull('parent_id') ->with('children') ->get(); $flatList = $this->flattenNodes($hierarchical);
? 提示:$node->relationLoaded('children') 可避免未预加载时触发 N+1 查询;$node->children 是 Collection,天然支持递归遍历。 ❌ array_walk_recursive() 不适用的原因 原问题中尝试的 array_walk_recursive() 无法满足需求,因其: 仅遍历 数组值 ,会丢失键名(如 'id'/'name' 字段名不可靠); 对 Eloquent 模型对象(非纯数组)遍历时行为不可控; 无法区分同名字段(如多层 name)或控制输出结构。 因此, 显式递归遍历模型集合是更安全、可维护的选择 。 总结 场景推荐方案关键代码仅需所有节点的 id+name全表 select() 查询Hierarchy::select('id','name')->get()已加载嵌套结构,需从中提取自定义递归扁平函数flattenNodes($collection)需保留父子上下文(如带 level/path)扩展递归函数传入深度参数flattenNodes($nodes, $level = 0) 最终,优先用数据库解决数据形态问题;复杂逻辑才交由 PHP 处理——这是 Laravel 应用性能与可维护性的黄金准则。

相关文章