126 lines
3.4 KiB
PHP
126 lines
3.4 KiB
PHP
<?php
|
||
|
||
namespace Aether\Traits;
|
||
|
||
use Aether\AetherModel;
|
||
use Hyperf\Database\Model\Collection;
|
||
use LogicException;
|
||
|
||
trait AetherTree
|
||
{
|
||
// 初始化时检查当前类是否继承AetherModel
|
||
public function __construct(array $attributes = [])
|
||
{
|
||
parent::__construct($attributes);
|
||
|
||
if (!$this instanceof AetherModel) {
|
||
throw new LogicException(
|
||
"使用AetherTree trait的类必须继承AetherModel,当前类: " . get_class($this)
|
||
);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 抽象方法:获取父ID字段名(由子类实现)
|
||
*/
|
||
abstract protected function getParentIdField(): string;
|
||
|
||
/**
|
||
* 抽象方法:获取排序字段名(由子类实现)
|
||
*/
|
||
abstract protected function getSortField(): string;
|
||
|
||
/**
|
||
* 构建树形结构
|
||
*/
|
||
public static function buildTree($items, int $parentId = 0): array
|
||
{
|
||
$self = new static();
|
||
$parentField = $self->getParentIdField();
|
||
$sortField = $self->getSortField();
|
||
|
||
$items = $items instanceof Collection ? $items->toArray() : $items;
|
||
$tree = [];
|
||
|
||
foreach ($items as $item) {
|
||
if ($item[$parentField] == $parentId) {
|
||
$children = static::buildTree($items, $item['id']);
|
||
if (!empty($children)) {
|
||
$item['children'] = $children;
|
||
}
|
||
$tree[] = $item;
|
||
}
|
||
}
|
||
|
||
$self->sortTreeItems($tree, $sortField);
|
||
return $tree;
|
||
}
|
||
|
||
/**
|
||
* 树形节点排序
|
||
*/
|
||
protected function sortTreeItems(array &$items, string $sortField): void
|
||
{
|
||
usort($items, function ($a, $b) use ($sortField) {
|
||
$direction = $this->treeSortDirection ?? 'asc';
|
||
return $direction === 'desc'
|
||
? $b[$sortField] <=> $a[$sortField]
|
||
: $a[$sortField] <=> $b[$sortField];
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 获取指定节点的所有子节点ID
|
||
*/
|
||
public function getChildIds(int $id): array
|
||
{
|
||
$parentField = $this->getParentIdField();
|
||
// 现在可以安全调用newQuery(),因为已通过继承检查
|
||
$allItems = $this->newQuery()->get(['id', $parentField])->toArray();
|
||
$ids = [$id];
|
||
|
||
$this->collectChildIds($allItems, $id, $parentField, $ids);
|
||
return $ids;
|
||
}
|
||
|
||
/**
|
||
* 递归收集子节点ID
|
||
*/
|
||
private function collectChildIds(array $items, int $parentId, string $parentField, array &$ids): void
|
||
{
|
||
foreach ($items as $item) {
|
||
if ($item[$parentField] == $parentId) {
|
||
$ids[] = $item['id'];
|
||
$this->collectChildIds($items, $item['id'], $parentField, $ids);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取节点的完整路径
|
||
*/
|
||
public function getPath(int $id): array
|
||
{
|
||
$parentField = $this->getParentIdField();
|
||
// 安全调用newQuery()
|
||
$node = $this->newQuery()->find($id);
|
||
if (!$node) {
|
||
return [];
|
||
}
|
||
|
||
$path = [$node->toArray()];
|
||
$parentId = $node[$parentField];
|
||
|
||
while ($parentId > 0) {
|
||
$parent = $this->newQuery()->find($parentId);
|
||
if (!$parent) {
|
||
break;
|
||
}
|
||
array_unshift($path, $parent->toArray());
|
||
$parentId = $parent[$parentField];
|
||
}
|
||
|
||
return $path;
|
||
}
|
||
}
|