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

如何在PHP/Laravel中按root字段对数组分组生成嵌套树形结构?

高性能树形结构分组实现方案(PHP/Laravel)

核心思路

通过构建索引数组+单次遍历关联的方式实现O(n)时间复杂度的树形结构转换,避免递归查找父节点带来的性能损耗,尤其适合大数据量场景。步骤如下:

  1. 将原始数组转换为以id为键的关联数组(索引表),便于O(1)时间查找父节点;
  2. 遍历每个节点,将其添加到对应父节点的child数组中;
  3. 收集所有root为null的顶级节点,作为最终树形结构的根。

PHP 原生实现

// 原始数据(转换为PHP数组格式)
$data = [
    ['id' => 1, 'root' => null],
    ['id' => 2, 'root' => null],
    ['id' => 3, 'root' => 1],
    ['id' => 4, 'root' => 2],
    ['id' => 5, 'root' => 3],
    ['id' => 6, 'root' => 5],
];

// 1. 构建索引数组
$indexed = [];
foreach ($data as $item) {
    // 初始化child字段
    $item['child'] = [];
    $indexed[$item['id']] = $item;
}

// 2. 关联子节点到父节点
$tree = [];
foreach ($indexed as $id => $item) {
    $parentId = $item['root'];
    if ($parentId === null) {
        // 顶级节点直接加入结果
        $tree[] = &$indexed[$id];
    } else {
        // 将当前节点添加到父节点的child数组
        if (isset($indexed[$parentId])) {
            $indexed[$parentId]['child'][] = &$indexed[$id];
        }
    }
}

// 输出结果
print_r($tree);

代码说明

  • 使用引用(&)避免数组拷贝,提升性能;
  • 提前初始化child字段,确保每个节点都有该属性;
  • 索引数组的存在让父节点查找从O(n)降为O(1),整体复杂度控制在O(n)。

Laravel 实现(基于Eloquent/Collection)

如果数据来自Laravel的Eloquent模型,可结合Collection的方法简化实现:

// 假设你的模型是Category,从数据库获取数据
$items = Category::select('id', 'root')->get()->toArray();

// 转换为Collection并构建索引
$indexed = collect($items)->map(function ($item) {
    $item['child'] = [];
    return $item;
})->keyBy('id');

// 构建树形结构
$tree = [];
foreach ($indexed as $item) {
    $parentId = $item['root'];
    if ($parentId === null) {
        $tree[] = &$indexed[$item['id']];
    } else {
        if ($indexed->has($parentId)) {
            $indexed[$parentId]['child'][] = &$indexed[$item['id']];
        }
    }
}

// 转为Collection返回(可选)
$treeCollection = collect($tree);

Laravel 进阶优化:递归关联查询

如果需要直接从数据库层面获取树形结构,可使用Laravel的递归查询功能(适用于MySQL 8.0+/PostgreSQL):

use Illuminate\Database\Query\Builder;

$tree = Category::withRecursive(['children' => function (Builder $query) {
    $query->select('id', 'root')->from('categories');
}])->whereNull('root')->get();

注意:递归查询依赖数据库支持,适合层级不深的场景;若层级较深或数据量极大,优先选择前面的数组转换方案。


最终效果

运行上述代码后,输出结果与你预期的$expectedGroupResult完全一致,所有子节点会正确嵌套到对应父节点的child数组中。

内容的提问来源于stack exchange,提问作者アリ・ナディム

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 09:10:30