PHP实现层级表中子节点值汇总至父节点的技术求助
PHP计算层级数组中子节点数值总和并赋值给父节点
问题背景
现有如下数据库表结构及数据:
| kia_id | kia_name | value | parent_id |
|---|---|---|---|
| 1 | ac service | 0 | |
| 2 | hil | 0 | |
| 3 | dispatch | 1 | |
| 4 | tat main | 13.3 | 3 |
| 5 | tat air | 10.1 | 3 |
| 6 | sla comp | 11.7 | 2 |
| 7 | sla serv | 2 | |
| 8 | slb | 9.9 | 7 |
需要生成带total_value_from_child列的HTML表格,该列的值为当前节点所有子节点(包括深层子节点)的value总和,最终效果如下:
| kia_id | kia_name | value | parent_id | total_value_from_child |
|---|---|---|---|---|
| 1 | ac service | 0 | 23.4 | |
| 3 | dispatch | 1 | 23.4 | |
| 4 | tat main | 13.3 | 3 | |
| 5 | tat air | 10.1 | 3 | |
| 2 | hil | 0 | 21.6 | |
| 6 | sla comp | 11.7 | 2 | |
| 7 | sla serv | 2 | 9.9 | |
| 8 | slb | 9.9 | 7 |
解决方案
我们可以通过构建树状结构 + 递归计算子节点总和的方式来实现,具体步骤如下:
1. 从数据库获取数据
首先连接数据库,把所有数据读取到一个数组中(这里以PDO为例,你也可以用mysqli):
// 数据库连接配置 $host = 'localhost'; $dbname = 'your_db_name'; $username = 'your_username'; $password = 'your_password'; try { $pdo = new PDO("mysql:host=$host;dbname=$dbname;charset=utf8", $username, $password); $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION); // 查询所有数据 $stmt = $pdo->query("SELECT kia_id, kia_name, value, parent_id FROM your_table_name"); $data = $stmt->fetchAll(PDO::FETCH_ASSOC); // 把value转换为数值类型,空值转为0 foreach ($data as &$item) { $item['value'] = empty($item['value']) ? 0 : (float)$item['value']; $item['total_value_from_child'] = ''; // 初始化总和字段 } } catch(PDOException $e) { die("数据库连接失败: " . $e->getMessage()); }
2. 构建树状层级结构
把扁平的数组转换成以parent_id为分组的结构,方便后续递归遍历:
// 构建节点映射表,方便快速查找节点 $nodes = []; foreach ($data as $item) { $nodes[$item['kia_id']] = $item; $nodes[$item['kia_id']]['children'] = []; // 初始化子节点数组 } // 构建树状结构 $tree = []; foreach ($data as $item) { if ($item['parent_id'] == 0) { $tree[] = &$nodes[$item['kia_id']]; } else { $nodes[$item['parent_id']]['children'][] = &$nodes[$item['kia_id']]; } }
3. 递归计算子节点总和
编写一个递归函数,遍历每个节点,计算其所有子节点的value总和,并赋值给当前节点的total_value_from_child:
function calculateChildTotal(&$node) { $total = 0; // 遍历子节点 foreach ($node['children'] as &$child) { // 递归计算子节点的子节点总和 $total += calculateChildTotal($child); // 加上当前子节点的value $total += $child['value']; } // 如果总和大于0,赋值给total_value_from_child,否则留空 if ($total > 0) { $node['total_value_from_child'] = number_format($total, 1); // 保留1位小数 } return $total; } // 遍历根节点(parent_id=0的节点),触发递归计算 foreach ($tree as &$rootNode) { calculateChildTotal($rootNode); }
4. 生成HTML表格
最后遍历树状结构,按层级输出HTML表格,子节点可以添加缩进以体现层级关系:
// 递归生成表格行 function generateTableRows($nodes, $indent = 0) { $rows = ''; $indentStr = str_repeat(' ', $indent * 4); // 每层缩进4个空格 foreach ($nodes as $node) { $rows .= "<tr> <td>{$node['kia_id']}</td> <td>{$indentStr}{$node['kia_name']}</td> <td>" . ($node['value'] > 0 ? $node['value'] : '') . "</td> <td>{$node['parent_id']}</td> <td>{$node['total_value_from_child']}</td> </tr>"; // 递归生成子节点的行 if (!empty($node['children'])) { $rows .= generateTableRows($node['children'], $indent + 1); } } return $rows; } // 输出完整的HTML表格 echo "<table border='1' cellpadding='8' cellspacing='0'> <thead> <tr> <th>kia_id</th> <th>kia_name</th> <th>value</th> <th>parent_id</th> <th>total_value_from_child</th> </tr> </thead> <tbody> " . generateTableRows($tree) . " </tbody> </table>";
代码说明
- 我们先把数据库数据转换成树状结构,方便层级遍历;
- 递归函数
calculateChildTotal会深度优先遍历每个节点,累加所有子节点的value(包括深层子节点); - 生成表格时通过缩进直观体现层级关系,和你期望的输出格式一致;
- 空的
value会被转为0参与计算,最终总和保留1位小数,符合示例中的数值格式。
内容的提问来源于stack exchange,提问作者aeeeeeeng
相关产品推荐
相关产品推荐

