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; } /** * 获取指定节点的所有子节点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; } /** * 获取节点的完整路径. */ 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; } /** * 抽象方法:获取父ID字段名(由子类实现). */ abstract protected function getParentIdField(): string; /** * 抽象方法:获取排序字段名(由子类实现). */ abstract protected function getSortField(): string; /** * 树形节点排序. */ 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. */ 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); } } } }