Tree.php 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. <?php
  2. declare(strict_types=1);
  3. namespace addons\shopro\library;
  4. use think\model\Collection;
  5. class Tree
  6. {
  7. protected $model = null;
  8. public function __construct($model)
  9. {
  10. $this->model = $model;
  11. }
  12. /**
  13. * 获取递归树
  14. *
  15. * @param integer|array|Collection $items 可以传入某个id 查询这个id的下级树,也可以传一个查询列表结果,将获取这个列表的所有的下级 树
  16. * @param \Closure $resultCb 用来处理每一次查询的结果 比如要取出树中的所有 name
  17. * @return Collection
  18. */
  19. public function getTree($items = 0, \Closure $resultCb = null)
  20. {
  21. if (!is_array($items) && !$items instanceof Collection) {
  22. $items = $this->getQuery()->where('parent_id', $items)->select();
  23. $resultCb && $items = $resultCb($items);
  24. }
  25. foreach ($items as $key => &$item) {
  26. $child = $this->getTree($item->id, $resultCb);
  27. if ($child) {
  28. $item->children = $child;
  29. }
  30. }
  31. return $items;
  32. }
  33. /**
  34. * 获取递归树(包含自身)
  35. *
  36. * @param integer $id
  37. * @param \Closure $resultCb 用来处理每一次查询的结果 比如要取出树中的所有 name
  38. * @return Collection
  39. */
  40. public function getChildren($id, \Closure $resultCb = null)
  41. {
  42. $self = $this->getQuery()->where('id', $id)->select();
  43. if(!$self) {
  44. error_stop('未找到数据');
  45. }
  46. $items = $this->getQuery()->where('parent_id', $id)->select();
  47. $resultCb && $items = $resultCb($items);
  48. foreach ($items as $key => &$item) {
  49. $child = $this->getTree($item->id, $resultCb);
  50. if ($child) {
  51. $item->children = $child;
  52. }
  53. }
  54. $self[0]->children = $items;
  55. return $self;
  56. }
  57. /**
  58. * 检测id 是不是自己的下级
  59. *
  60. * @param [type] $parent_id
  61. * @param [type] $id
  62. * @return void
  63. */
  64. public function checkParent($parent_id, $id)
  65. {
  66. if ($parent_id == $id) {
  67. error_stop('当前上级不能是自己');
  68. }
  69. $childIds = $this->getChildIds($id);
  70. if (in_array($parent_id, $childIds)) {
  71. error_stop('当前上级不能是自己的下级');
  72. }
  73. return true;
  74. }
  75. /**
  76. * 获取当前对象所属级别
  77. *
  78. * @param [type] $object
  79. * @return void
  80. */
  81. public function getLevel($object)
  82. {
  83. $parentIds = $this->getParentFields($object, 'id');
  84. return count($parentIds);
  85. }
  86. /**
  87. * 缓存递归获取当前对象的上级 指定字段
  88. *
  89. * @param \think\Model|int $id
  90. * @param boolean $self 是否包含自己
  91. * @return array
  92. */
  93. public function getParentFields($item, $field = 'id', $self = true)
  94. {
  95. if (!$item instanceof \think\Model) {
  96. $item = $this->getQuery()->find($item);
  97. if (!$item) {
  98. return [];
  99. }
  100. }
  101. // 判断缓存
  102. $cacheKey = 'object-' . $this->getTable() . '-' . $item->id . '-' . $field . '-parent-ids';
  103. $objectIds = cache($cacheKey);
  104. if (!$objectIds) {
  105. $objectIds = array_reverse($this->recursionGetParentFields($item, $field));
  106. if ($self) {
  107. $objectIds[] = $item[$field]; // 加上自己
  108. }
  109. // 缓存暂时注释,如果需要,可以打开,请注意后台更新角色记得清除缓存
  110. // cache($cacheKey, $objectIds, (600 + mt_rand(0, 300))); // 加入随机秒数,防止一起全部过期
  111. }
  112. return $objectIds;
  113. }
  114. /**
  115. * 递归获取所有上级 id
  116. */
  117. private function recursionGetParentFields($item, $field = 'id', $ids = [])
  118. {
  119. if ($item->parent_id) {
  120. $parent = $this->getQuery()->find($item->parent_id);
  121. if ($parent) {
  122. $ids[] = $parent[$field];
  123. return $this->recursionGetParentFields($parent, $field, $ids);
  124. }
  125. }
  126. return $ids;
  127. }
  128. /**
  129. * 缓存递归获取子对象 id
  130. *
  131. * @param int $id 要查询的 id
  132. * @param boolean $self 是否包含自己
  133. * @return array
  134. */
  135. public function getChildIds($id, $self = true)
  136. {
  137. // 判断缓存
  138. $cacheKey = 'object-' . $this->getTable() . '-' . $id . '-child-ids';
  139. $objectIds = cache($cacheKey);
  140. if (!$objectIds) {
  141. $objectIds = $this->recursionGetChildIds($id, $self);
  142. // 缓存暂时注释,如果需要,可以打开,请注意后台更新角色记得清除缓存
  143. // cache($cacheKey, $objectIds, (600 + mt_rand(0, 300))); // 加入随机秒数,防止一起全部过期
  144. }
  145. return $objectIds;
  146. }
  147. /**
  148. * 递归获取子分类 id
  149. *
  150. */
  151. private function recursionGetChildIds($id, $self)
  152. {
  153. $ids = $self ? [$id] : [];
  154. $childrenIds = $this->getQuery()->where(['parent_id' => $id])->column('id');
  155. if ($childrenIds) {
  156. foreach ($childrenIds as $v) {
  157. $grandsonIds = $this->recursionGetChildIds($v, true);
  158. $ids = array_merge($ids, $grandsonIds);
  159. }
  160. }
  161. return $ids;
  162. }
  163. /**
  164. * 获取当前 查询
  165. *
  166. * @return think\model|think\db\Query
  167. */
  168. private function getQuery()
  169. {
  170. if ($this->model instanceof \Closure) {
  171. return ($this->model)();
  172. }
  173. return $this->model;
  174. }
  175. /**
  176. * 获取表
  177. */
  178. private function getTable()
  179. {
  180. $query = $this->getQuery();
  181. if ($query instanceof \think\Model) {
  182. $table_name = $query->getQuery()->getTable();
  183. } else {
  184. $table_name = $query->getTable();
  185. }
  186. return $table_name;
  187. }
  188. }