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

PHP层级树数组遍历及_children追加员工数据方案咨询

问题根源

现有buildHierarchyDepartment方法递归时直接为_children字段赋值子部门数组,会覆盖字段内提前存入的员工数据;而Tabulator组件本身支持_children字段下混合存放部门、员工两类节点,不存在格式兼容问题。

实现方案

方案1:修改原有构建逻辑,构建阶段直接合并员工(性能最优)

不需要分两次遍历树,在原有部门构建逻辑里直接合并员工数据,从根源避免_children被覆盖的问题,适合可以动原有构建方法的场景。

  1. 先做一次预处理,把所有员工按所属部门ID分组,避免遍历树时重复查找员工:
// 预处理员工数据,按所属部门ID分组
$employeeMap = [];
foreach ($employeeList as $emp) {
    $emp['_type'] = 'employee'; // 加类型标识,后续Tabulator渲染时可区分部门/员工
    $employeeMap[$emp['department_id']][] = $emp;
}
  1. 修改原有部门构建方法,统一合并子部门和员工数据到_children:
private function buildHierarchyDepartment(array $elements, array $employeeMap, $parentId = 0) {
    $branch = [];
    foreach ($elements as $element) {
        if ($element['parent_id'] == $parentId) {
            $element['_type'] = 'department'; // 标记节点类型为部门
            // 递归获取当前部门下的所有子部门
            $childDepts = static::buildHierarchyDepartment($elements, $employeeMap, $element['id']);
            // 取当前部门下的员工,无员工则取空数组
            $currentEmps = $employeeMap[$element['id']] ?? [];
            // 合并员工和子部门到_children,调整参数顺序可改变展示顺序:员工放前面则先显示员工,子部门放前面则先显示子部门
            $element['_children'] = array_merge($currentEmps, $childDepts);
            $branch[] = $element;
        }
    }
    return $branch;
}

方案2:保留原有构建逻辑,树构建完成后单独追加员工(改动最小)

如果不想修改已经调试好的部门构建方法,可以先按原有逻辑生成纯部门树,再单独写递归逻辑往每个节点追加员工。

  1. 同方案1,先把员工按部门ID分组为$employeeMap
  2. 新增员工追加方法,引用传递直接修改原树结构:
/**
 * 往已构建完成的部门树中追加员工数据
 * @param array $deptTree 构建好的纯部门层级树,引用传递
 * @param array $employeeMap 按部门ID分组的员工数组
 */
private function appendEmployeesToDeptTree(array &$deptTree, array $employeeMap) {
    foreach ($deptTree as &$node) {
        // 处理当前部门节点:存在下属员工则追加到_children
        if (isset($employeeMap[$node['id']])) {
            // 无_children字段则先初始化
            if (!isset($node['_children'])) {
                $node['_children'] = [];
            }
            // 合并员工到子节点列表,调整参数顺序可修改展示优先级
            $node['_children'] = array_merge($employeeMap[$node['id']], $node['_children']);
        }
        // 存在子节点则递归处理下一层
        if (!empty($node['_children'])) {
            static::appendEmployeesToDeptTree($node['_children'], $employeeMap);
        }
    }
    unset($node); // 解除引用避免后续逻辑出现数据污染
}
  1. 调用示例:
// 按原有逻辑生成纯部门树
$deptTree = $this->buildHierarchyDepartment($allDepts);
// 追加员工数据
$this->appendEmployeesToDeptTree($deptTree, $employeeMap);
注意事项
  • 给节点加_type字段区分部门/员工是Tabulator的通用实践,后续自定义行样式、绑定点击事件、做字段格式化时可以直接用这个字段判断节点类型,避免逻辑混淆
  • 两种方案都是递归实现,当前业务节点最深仅5层,完全不会出现PHP栈溢出问题,不需要额外改写成非递归的广度/深度优先遍历,优先保证代码可读性即可
  • 提前把员工按部门ID分组是必要的性能优化,不要在树遍历的循环里每次全量匹配员工数据,会把时间复杂度从O(n+m)拉高到O(n*m),数据量大时性能差异明显

内容的提问来源于stack exchange,提问作者Franz Justin Buenaventura

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 12:57:18