PHP实现无限级上级ID回溯与树形分类递归
在处理具有层级关系的数据时,PHP开发常需处理两种场景:根据子节点反向追溯所有父节点ID,或将扁平列表转换为树形结构以展示分类。本文将分别针对这两种情况提供基于递归的高效解决方案,涵盖数据库字段设计、核心函数逻辑及实际调用方式。
场景一:递归获取无限上级ID
当数据表采用自引用结构(如 auth_id 指向父级ID)且需要获取某个节点的所有祖先ID时,可通过SQL查询结合PHP递归实现。此方法适用于权限链、组织架构图等需要向上追溯的业务场景。
准备条件:数据库结构设计
首先,确保表中存在指向父级的字段。例如 fcxlt_authuser 表中,id 为主键,auth_id 存储上级用户的ID。递归的终止条件通常设定为 ID=1(根节点)或特定标识。
CREATE TABLE `fcxlt_authuser` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(64) NOT NULL COMMENT '密码',
`nickname` varchar(50) NOT NULL COMMENT '昵称',
`phone` varchar(20) DEFAULT NULL COMMENT '电话',
`weixin_id` varchar(20) DEFAULT NULL COMMENT '微信号',
`qrcode` varchar(200) DEFAULT NULL COMMENT '二维码Base64',
`auth_id` int(11) NOT NULL COMMENT '上级用户ID',
`is_authed` int(11) NOT NULL,
`createtime` int(11) NOT NULL DEFAULT '1523156898',
PRIMARY KEY (`id`),
KEY `nickname` (`nickname`),
KEY `auth_id` (`auth_id`,`id`,`username`,`nickname`) USING BTREE,
KEY `username` (`username`,`is_authed`,`password`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=103 DEFAULT CHARSET=utf8mb4;
第1步:编写递归追溯函数
创建一个递归方法,每次查询当前节点的 auth_id,并将该ID追加到结果字符串中,直到遇到根节点。注意:必须在递归调用处使用 return,否则函数可能返回 NULL 导致逻辑中断。
public function getWinfo($id, $res = '') {
// 假设ID大于1表示非根节点,可根据实际需求调整终止条件
if ($id > 1) {
$infos = Db::name('authuser')
->alias('a')
->where('a.id', $id)
->join('fcxlt_authuser b', 'a.auth_id=b.id', 'LEFT')
->field('a.auth_id as id')
->find();
// 如果查到了上级信息且上级不是根节点(这里假设1为根)
if (!empty($infos) && $infos['id'] > 1) {
$res .= $infos['id'] . ",";
// 关键:必须return递归调用的结果
return $this->getWinfo($infos['id'], $res);
} else {
// 到达根节点或结束,返回累积的字符串
return $res;
}
} else {
return $res;
}
}
第2步:控制器中调用并解析结果
在控制器中传入目标ID,函数返回以逗号分隔的上级ID字符串。最后通过截取和分割,得到纯数字数组供后续使用。
// 初始调用,可将当前ID也加入字符串以便处理边界
$ids = $this->getWinfo($targetId, $targetId . ',');
// 去掉末尾多余的逗号
$id_in = substr($ids, 0, strlen($ids) - 1);
// 转换为整数数组
$id_arr = explode(',', $id_in);
场景二:递归实现无限极分类树
另一种常见需求是将数据库中的平铺数据(包含ID和PID)转换为嵌套的树形数组,便于前端渲染分类菜单。这需要从内存数据中进行递归匹配。
准备条件:数据模型假设
数据源为包含 id, pid, name 字段的数组或从数据库查出的列表。例如:
| ID | PID | 名称 |
|---|---|---|
| 1 | 0 | 汽车 |
| 2 | 1 | 欧拉 |
| 3 | 1 | 吉利 |
| 4 | 3 | 沃尔沃 |
| 5 | 1 | 大众 |
第1步:构建递归树生成函数
利用静态变量 $list 避免每次递归重新声明导致的覆盖问题。核心逻辑是:遍历未处理的节点,找到 pid 等于当前查找ID的节点,将其加入结果集,然后以其ID为新父ID继续递归。
/**
* 递归实现无限极分类
* @param array $array 所有分类数据
* @param int $pid 父ID
* @param int $level 层级深度,用于格式化显示
* @return array $list 构建树形后的数组
*/
function getTree($array, $pid = 0, $level = 0) {
// 声明静态数组,防止多次递归调用时重置
static $list = [];
foreach ($array as $key => $value) {
// 如果当前节点的父ID等于我们要找的父ID
if ($value['pid'] == $pid) {
// 添加层级属性,方便后续缩进显示
$value['level'] = $level;
$list[] = $value;
// 将该节点从数组中移除,减少后续无用的遍历消耗
unset($array[$key]);
// 递归查找该节点的子节点,层级+1
getTree($array, $value['id'], $level + 1);
}
}
return $list;
}
第2步:调用并输出结果
执行函数后,将得到的树形数组进行遍历。可利用 level 属性控制缩进,直观展示层级关系。
// 假设 $sourceData 是从数据库取出的所有分类数据
$resultTree = getTree($sourceData);
foreach ($resultTree as $item) {
// str_repeat 生成缩进符,level=0无缩进,level=1一个'--',以此类推
echo str_repeat('--', $item['level']) . $item['name'] . '
';
}
输出效果如下:
河北省
--邯郸市
----永年区
--武安市
北京市
--朝阳区
----望京
----酒仙桥
--通州区
辅助技巧:Null值转空字符串
在处理上述数据结构时,若数据库中某些字段可能为 NULL,为避免JSON序列化或界面展示出错,可使用工具函数将数组中的 NULL 统一转换为空字符串。
public function convertNullToEmptyString($data) {
return collect($data)->map(function ($value) {
if (is_null($value)) {
return '';
}
return is_array($value) ? $this->convertNullToEmptyString($value) : $value;
})->toArray();
}
// 测试用例
$data = [
['name' => 'shawn', 'email' => 'test@test.com'],
['name' => null, 'company' => 'Test Co']
];
var_dump($this->convertNullToEmptyString($data));
以上就是PHP递归实现无限上级ID回溯与树形分类的详细内容,更多关于PHP层级数据处理与递归算法的资料请关注本站其它相关文章!
免责声明:本文内容仅供参考,不代表本站观点。转载或引用请注明出处。









