Files
fdesk/app/common/utils/Tree.php
T
2025-08-11 20:09:28 +08:00

320 lines
10 KiB
PHP

<?php
namespace app\common\utils;
class Tree
{
/**
* 递归构建树形结构
*
* @param array $data 数据列表
* @param int $parentId 父级ID
* @param string $pidField 父级字段名
* @param string $idField ID字段名
* @param int $level 当前层级
* @return array
*/
public static function build(array $data, int $parentId = 0, string $pidField = 'parent_id', string $idField = 'id', int $level = 0): array
{
$tree = [];
foreach ($data as $item) {
if ($item[$pidField] == $parentId) {
$item['level'] = $level;
$children = static::build($data, $item[$idField], $pidField, $idField, $level + 1);
if (!empty($children)) {
$item['children'] = $children;
}
$tree[] = $item;
}
}
return $tree;
}
/**
* 使用引用方式构建树形结构(性能更好)
*
* @param array $data 数据列表
* @param string $pidField 父级字段名
* @param string $idField ID字段名
* @return array
*/
public static function buildWithReference(array $data, string $pidField = 'parent_id', string $idField = 'id'): array
{
$items = [];
$tree = [];
// 建立以id为索引的数组
foreach ($data as $item) {
$item['level'] = 0;
$items[$item[$idField]] = $item;
}
// 构建树形结构
foreach ($items as $id => $item) {
if ($item[$pidField] == 0) {
$tree[] = &$items[$id];
} else {
if (isset($items[$item[$pidField]])) {
$items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
if (!isset($items[$item[$pidField]]['children'])) {
$items[$item[$pidField]]['children'] = [];
}
$items[$item[$pidField]]['children'][] = &$items[$id];
}
}
}
return $tree;
}
/**
* 将树形结构扁平化为列表(带层级前缀)
*
* @param array $tree 树形结构数据
* @param string $prefix 前缀字符
* @param string $field 显示字段名
* @param int $level 层级
* @return array
*/
public static function flattenWithPrefix(array $tree, string $prefix = '--', string $field = 'name', int $level = 0): array
{
$result = [];
foreach ($tree as $item) {
$item[$field . '_with_prefix'] = str_repeat($prefix, $item['level']) . $item[$field];
$result[] = $item;
if (isset($item['children']) && !empty($item['children'])) {
$children = static::flattenWithPrefix($item['children'], $prefix, $field, $item['level'] + 1);
$result = array_merge($result, $children);
}
}
return $result;
}
/**
* 根据父ID查找所有子节点(包括子孙节点)
*
* @param array $data 数据列表
* @param int $parentId 父级ID
* @param string $pidField 父级字段名
* @param string $idField ID字段名
* @return array
*/
public static function findChildren(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
{
$children = [];
foreach ($data as $item) {
if ($item[$pidField] == $parentId) {
$children[] = $item;
$grandChildren = static::findChildren($data, $item[$idField], $pidField, $idField);
$children = array_merge($children, $grandChildren);
}
}
return $children;
}
/**
* 获取指定节点的所有父级节点
*
* @param array $data 数据列表
* @param int $id 当前节点ID
* @param string $pidField 父级字段名
* @param string $idField ID字段名
* @return array
*/
public static function getParents(array $data, int $id, string $pidField = 'parent_id', string $idField = 'id'): array
{
$parents = [];
foreach ($data as $item) {
if ($item[$idField] == $id) {
if ($item[$pidField] != 0) {
$parents[] = $item;
$parentParents = static::getParents($data, $item[$pidField], $pidField, $idField);
$parents = array_merge($parents, $parentParents);
}
break;
}
}
return $parents;
}
/**
* 获取指定节点的子孙节点ID列表
*
* @param array $data 数据列表
* @param int $parentId 父级ID
* @param string $pidField 父级字段名
* @param string $idField ID字段名
* @return array
*/
public static function getChildrenIds(array $data, int $parentId, string $pidField = 'parent_id', string $idField = 'id'): array
{
$ids = [];
$children = static::findChildren($data, $parentId, $pidField, $idField);
foreach ($children as $child) {
$ids[] = $child[$idField];
}
return $ids;
}
/**
* 检查是否有子节点
*
* @param array $data 数据列表
* @param int $parentId 父级ID
* @param string $pidField 父级字段名
* @return bool
*/
public static function hasChildren(array $data, int $parentId, string $pidField = 'parent_id'): bool
{
foreach ($data as $item) {
if ($item[$pidField] == $parentId) {
return true;
}
}
return false;
}
/**
* 构建带有权限列表的资源树(权限不显示在树结构中)
*
* @param array $data 资源数据列表
* @param int $parentId 父级ID
* @param string $pidField 父级字段名
* @param string $idField ID字段名
* @param string $typeField 类型字段名
* @param string $permissionType 权限类型标识
* @param int $level 当前层级
* @return array
*/
public static function buildWithAuthList(
array $data,
int $parentId = 0,
string $pidField = 'parent_id',
string $idField = 'id',
string $typeField = 'type',
string $permissionType = 'permission',
int $level = 0
): array {
$tree = [];
foreach ($data as $item) {
// 只处理指定父级的非权限节点
if ($item[$pidField] == $parentId && $item[$typeField] != $permissionType) {
$item['level'] = $level;
// 递归处理子节点(非权限类型)
$children = static::buildWithAuthList($data, $item[$idField], $pidField, $idField, $typeField, $permissionType, $level + 1);
if (!empty($children)) {
$item['children'] = $children;
}
// 收集当前节点下的所有权限
$item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
$tree[] = $item;
}
}
return $tree;
}
/**
* 收集指定节点下的直接权限项(只收集直接子节点中的权限)
*
* @param array $data 数据列表
* @param int $parentId 父级ID
* @param string $pidField 父级字段名
* @param string $typeField 类型字段名
* @param string $permissionType 权限类型标识
* @return array
*/
public static function collectDirectPermissions(
array $data,
int $parentId,
string $pidField = 'parent_id',
string $typeField = 'type',
string $permissionType = 'permission'
): array {
$permissions = [];
foreach ($data as $item) {
// 收集直接子节点中的权限类型
if ($item[$pidField] == $parentId && $item[$typeField] == $permissionType) {
$permissions[] = $item;
}
}
return $permissions;
}
/**
* 使用引用方式构建带有权限列表的资源树(权限不显示在树结构中)
*
* @param array $data 资源数据列表
* @param string $pidField 父级字段名
* @param string $idField ID字段名
* @param string $typeField 类型字段名
* @param string $permissionType 权限类型标识
* @return array
*/
public static function buildWithAuthListAndReference(
array $data,
string $pidField = 'parent_id',
string $idField = 'id',
string $typeField = 'type',
string $permissionType = 'permission'
): array {
$items = [];
$tree = [];
// 建立以id为索引的数组(只包含非权限节点)
foreach ($data as $item) {
if ($item[$typeField] != $permissionType) {
$item['level'] = 0;
$items[$item[$idField]] = $item;
}
}
// 构建树形结构
foreach ($items as $id => $item) {
if ($item[$pidField] == 0) {
$tree[] = &$items[$id];
} else {
if (isset($items[$item[$pidField]])) {
$items[$id]['level'] = $items[$item[$pidField]]['level'] + 1;
if (!isset($items[$item[$pidField]]['children'])) {
$items[$item[$pidField]]['children'] = [];
}
$items[$item[$pidField]]['children'][] = &$items[$id];
}
}
}
// 为每个节点添加权限列表
foreach ($items as &$item) {
$item['auth_list'] = static::collectDirectPermissions($data, $item[$idField], $pidField, $typeField, $permissionType);
}
return $tree;
}
}