123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194 |
- <?php
- namespace app\common;
- class Tree
- {
- /**
- * 获取完整的树结构,包含祖先节点
- */
- const INCLUDE_ANCESTORS = 1;
- /**
- * 获取部分树,不包含祖先节点
- */
- const EXCLUDE_ANCESTORS = 0;
- /**
- * 数据
- * @var array
- */
- protected $data = [];
- /**
- * 哈希树
- * @var array
- */
- protected $hashTree = [];
- /**
- * 父级字段名
- * @var string
- */
- protected $pidName = 'pid';
- /**
- * @param $data
- * @param string $pid_name
- */
- public function __construct($data, string $pid_name = 'pid')
- {
- $this->pidName = $pid_name;
- if (is_object($data) && method_exists($data, 'toArray')) {
- $this->data = $data->toArray();
- } else {
- $this->data = (array)$data;
- $this->data = array_map(function ($item) {
- if (is_object($item) && method_exists($item, 'toArray')) {
- return $item->toArray();
- }
- return $item;
- }, $this->data);
- }
- $this->hashTree = $this->getHashTree();
- }
- /**
- * 获取子孙节点
- * @param array $include
- * @param bool $with_self
- * @return array
- */
- public function getDescendant(array $include, bool $with_self = false): array
- {
- $items = [];
- foreach ($include as $id) {
- if (!isset($this->hashTree[$id])) {
- return [];
- }
- if ($with_self) {
- $item = $this->hashTree[$id];
- unset($item['children']);
- $items[$item['id']] = $item;
- }
- foreach ($this->hashTree[$id]['children'] ?? [] as $item) {
- unset($item['children']);
- $items[$item['id']] = $item;
- foreach ($this->getDescendant([$item['id']]) as $it) {
- $items[$it['id']] = $it;
- }
- }
- }
- return array_values($items);
- }
- /**
- * 获取哈希树
- * @param array $data
- * @return array
- */
- protected function getHashTree(array $data = []): array
- {
- $data = $data ?: $this->data;
- $hash_tree = [];
- foreach ($data as $item) {
- $hash_tree[$item['id']] = $item;
- }
- foreach ($hash_tree as $index => $item) {
- if ($item[$this->pidName] && isset($hash_tree[$item[$this->pidName]])) {
- $hash_tree[$item[$this->pidName]]['children'][$hash_tree[$index]['id']] = &$hash_tree[$index];
- }
- }
- return $hash_tree;
- }
- /**
- * 获取树
- * @param array $include
- * @param int $type
- * @return array|null
- */
- public function getTree(array $include = [], int $type = 1): ?array
- {
- // $type === static::EXCLUDE_ANCESTORS
- if ($type === static::EXCLUDE_ANCESTORS) {
- $items = [];
- $include = array_unique($include);
- foreach ($include as $id) {
- if (!isset($this->hashTree[$id])) {
- return [];
- }
- $items[] = $this->hashTree[$id];
- }
- return static::arrayValues($items);
- }
- // $type === static::INCLUDE_ANCESTORS
- $hash_tree = $this->hashTree;
- $items = [];
- if ($include) {
- $map = [];
- foreach ($include as $id) {
- if (!isset($hash_tree[$id])) {
- continue;
- }
- $item = $hash_tree[$id];
- $max_depth = 100;
- while ($max_depth-- > 0 && $item[$this->pidName] && isset($hash_tree[$item[$this->pidName]])) {
- $last_item = $item;
- $pid = $item[$this->pidName];
- $item = $hash_tree[$pid];
- $item_id = $item['id'];
- if (empty($map[$item_id])) {
- $map[$item_id] = 1;
- $hash_tree[$pid]['children'] = [];
- }
- $hash_tree[$pid]['children'][$last_item['id']] = $last_item;
- $item = $hash_tree[$pid];
- }
- $items[$item['id']] = $item;
- }
- } else {
- $items = $hash_tree;
- }
- $formatted_items = [];
- foreach ($items as $item) {
- if (!$item[$this->pidName] || !isset($hash_tree[$item[$this->pidName]])) {
- $formatted_items[] = $item;
- }
- }
- return static::arrayValues($formatted_items);
- }
- /**
- * 递归重建数组下标
- * @param $array
- * @return array
- */
- public static function arrayValues($array): array
- {
- if (!$array) {
- return [];
- }
- if (!isset($array['children'])) {
- $current = current($array);
- if (!is_array($current)) {
- return $array;
- }
- $tree = array_values($array);
- foreach ($tree as $index => $item) {
- $tree[$index] = static::arrayValues($item);
- }
- return $tree;
- }
- $array['children'] = array_values($array['children']);
- foreach ($array['children'] as $index => $child) {
- $array['children'][$index] = static::arrayValues($child);
- }
- return $array;
- }
- }
|