TreeTrait.php 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. <?php
  2. /**
  3. * Created by PhpStorm.
  4. * Author: 芸众商城 www.yunzshop.com
  5. * Date: 16/03/2017
  6. * Time: 08:58
  7. */
  8. namespace app\common\traits;
  9. use ArrayAccess;
  10. use BadMethodCallException;
  11. use Illuminate\Support\Collection;
  12. use InvalidArgumentException;
  13. /**
  14. * Class TreeTrait
  15. *
  16. * 使用
  17. * <?php
  18. * namespace app\common\models;
  19. * use app\common\traits\TreeTrait;
  20. *
  21. * class MyClass
  22. * {
  23. * use TreeTrait;
  24. *
  25. * // 自定义属性(可选)
  26. * protected $treeNodeIdName = 'id';
  27. * protected $treeNodeParentIdName = 'parent_id';
  28. * protected $treeNodeDisplayName = 'name';
  29. * protected $treeSpacer = '&nbsp;&nbsp;&nbsp;';
  30. * protected $treeFirstIcon = '&nbsp;&nbsp;&nbsp;│ ';
  31. * protected $treeMiddleIcon = '&nbsp;&nbsp;&nbsp;├─ ';
  32. * protected $treeLastIcon = '&nbsp;&nbsp;&nbsp;└─ ';
  33. * /**
  34. * * 获取待处理的原始节点数据
  35. * *
  36. * * 必须实现
  37. * *
  38. * * return \Illuminate\Support\Collection
  39. * *
  40. * public function getTreeAllNodes()
  41. * {
  42. *
  43. * }
  44. * }
  45. *
  46. * 可用方法
  47. * ```php
  48. *
  49. * public function setAllNodes(Collection $nodes)
  50. * public function getSubLevel($parentId)
  51. * public function getDescendants($parentId, $depth = 0, $adds = '')
  52. * public function getLayerOfDescendants($id)
  53. * public function getSelf($id)
  54. * public function getParent($id)
  55. * public function getAncestors($id, $depth = 0)
  56. *```
  57. * @package app\common\traits
  58. */
  59. trait TreeTrait
  60. {
  61. private $_allNodes = null;
  62. protected $treeNodeIdName = 'id';
  63. protected $treeNodeParentIdName = 'parent_id';
  64. protected $treeNodeDisplayName = 'name';
  65. protected $treeSpacer = '&nbsp;';
  66. protected $treeFirstIcon = '&nbsp;│ ';
  67. protected $treeMiddleIcon = '&nbsp;├─ ';
  68. protected $treeLastIcon = '&nbsp;└─ ';
  69. /**
  70. * 数据主ID名.
  71. *
  72. * @return string
  73. */
  74. protected function getTreeNodeIdName()
  75. {
  76. return property_exists($this, 'treeNodeIdName') ? $this->treeNodeIdName : 'id';
  77. }
  78. /**
  79. * 数据名称显示字段.
  80. *
  81. * @return string
  82. */
  83. protected function getTreeNodeDisplayName()
  84. {
  85. return property_exists($this, 'treeNodeDisplayName') ? $this->treeNodeDisplayName : 'name';
  86. }
  87. /**
  88. * 数据父ID名.
  89. *
  90. * @return string
  91. */
  92. protected function getTreeNodeParentIdName()
  93. {
  94. return property_exists($this, 'treeNodeParentIdName') ? $this->treeNodeParentIdName
  95. : 'parent_id';
  96. }
  97. protected function getTreeSpacer()
  98. {
  99. return property_exists($this, 'treeSpacer') ? $this->treeSpacer : ' ';
  100. }
  101. protected function getTreeFirstIcon()
  102. {
  103. return property_exists($this, 'treeFirstIcon') ? $this->treeFirstIcon
  104. : ' │ ';
  105. }
  106. protected function getTreeMiddleIcon()
  107. {
  108. return property_exists($this, 'treeMiddleIcon') ? $this->treeMiddleIcon
  109. : ' ├─ ';
  110. }
  111. protected function getTreeLastIcon()
  112. {
  113. return property_exists($this, 'treeLastIcon') ? $this->treeLastIcon
  114. : ' └─ ';
  115. }
  116. /**
  117. * 获取待格式树结构的节点数据.
  118. *
  119. * @return mixed
  120. */
  121. final protected function getAllNodes()
  122. {
  123. if ($this->_allNodes) {
  124. return $this->_allNodes;
  125. }
  126. if (!method_exists($this, 'getTreeAllNodes')) {
  127. throw new BadMethodCallException('Method [getTreeAllNodes] does not exist.');
  128. }
  129. $data = $this->getTreeAllNodes(); // 由use的class来实现
  130. if (!$data instanceof ArrayAccess) {
  131. throw new InvalidArgumentException('tree data must be a collection');
  132. }
  133. // 重置键值
  134. $this->_allNodes = collect([]);
  135. foreach ($data as $item) {
  136. $this->_allNodes->put($item->{$this->getTreeNodeIdName()}, $item);
  137. }
  138. return $this->_allNodes;
  139. }
  140. /**
  141. * 设置 所有节点.
  142. *
  143. * @param \Illuminate\Support\Collection $nodes
  144. */
  145. public function setAllNodes(Collection $nodes)
  146. {
  147. $this->_allNodes = $nodes;
  148. }
  149. /**
  150. * 获取子级(仅子代一级).
  151. *
  152. * @param mixed $parentId
  153. *
  154. * @return array
  155. */
  156. public function getSubLevel($parentId)
  157. {
  158. $data = $this->getAllNodes();
  159. $childList = collect([]);
  160. foreach ($data as $val) {
  161. if ($val->{$this->getTreeNodeParentIdName()} == $parentId) {
  162. $childList->put($val->{$this->getTreeNodeIdName()}, $val);
  163. }
  164. }
  165. return $childList;
  166. }
  167. /**
  168. * 获取指定节点的所有后代.
  169. *
  170. * @param mixed $parentId
  171. * @param int $depth
  172. * @param string $adds
  173. *
  174. * @return \Illuminate\Support\Collection
  175. */
  176. public function getDescendants($parentId, $depth = 0, $adds = '')
  177. {
  178. static $array;
  179. if (!$array instanceof ArrayAccess || $depth == 0) {
  180. $array = collect([]);
  181. }
  182. $number = 1;
  183. $child = $this->getSubLevel($parentId);
  184. if ($child) {
  185. $nextDepth = $depth + 1;
  186. $total = $child->count();
  187. foreach ($child as $val) {
  188. $j = $k = '';
  189. if ($number == $total) {
  190. $j .= $this->getTreeLastIcon();
  191. $k = $this->getTreeSpacer();
  192. } else {
  193. $j .= $this->getTreeMiddleIcon();
  194. $k = $adds ? $this->getTreeFirstIcon() : '';
  195. }
  196. $val->spacer = $adds ? ($adds . $j) : '';
  197. $val->depth = $depth;
  198. $array->put($val->{$this->getTreeNodeIdName()}, $val);
  199. $this->getDescendants(
  200. $val->{$this->getTreeNodeIdName()},
  201. $nextDepth,
  202. $adds . $k . $this->getTreeSpacer()
  203. );
  204. ++$number;
  205. }
  206. }
  207. return $array;
  208. }
  209. /**
  210. * 格式化为下拉选择数据
  211. * @param $parentId
  212. * @param int $depth
  213. * @param string $adds
  214. * @return array
  215. */
  216. public function toSelectArray($parentId, $depth = 0, $adds = '')
  217. {
  218. $treeList = [];
  219. $allTrees = $this->getDescendants($parentId, $depth, $adds);
  220. if ($allTrees) {
  221. foreach ($allTrees as $value) {
  222. $id = $value->{$this->getTreeNodeIdName()};
  223. $treeList[$id] = $value->spacer . $value->{$this->getTreeNodeDisplayName()};
  224. }
  225. }
  226. return $treeList;
  227. }
  228. /**
  229. * 获取指定节点的所有后代(分层级).
  230. *
  231. * @param mixed $id
  232. *
  233. * @return \Illuminate\Support\Collection
  234. */
  235. public function getLayerOfDescendants($id)
  236. {
  237. $child = $this->getSubLevel($id);
  238. $data = collect([]);
  239. if ($child) {
  240. foreach ($child as $val) {
  241. $val->child = $this->getLayerOfDescendants($val->{$this->getTreeNodeIdName()});
  242. $data->put($val->{$this->getTreeNodeIdName()}, $val);
  243. }
  244. }
  245. return $data;
  246. }
  247. /**
  248. * 获取指定id的数据.
  249. *
  250. * @param mixed $id
  251. *
  252. * @return mixed
  253. */
  254. public function getSelf($id)
  255. {
  256. $data = $this->getAllNodes();
  257. return $data->get($id);
  258. }
  259. /**
  260. * 获取父一级节点.
  261. *
  262. * @param mixed $id
  263. *
  264. * @return mixed
  265. */
  266. public function getParent($id)
  267. {
  268. $node = $this->getSelf($id);
  269. if ($node) {
  270. $parentId = $node->{$this->getTreeNodeParentIdName()};
  271. return $this->getSelf($parentId);
  272. }
  273. }
  274. /**
  275. * 获取节点的所有祖先.
  276. *
  277. * @param int $id
  278. * @param int $depth
  279. *
  280. * @return array
  281. */
  282. public function getAncestors($id, $depth = 0)
  283. {
  284. static $array;
  285. if (!$array instanceof ArrayAccess || $depth == 0) {
  286. $array = collect([]);
  287. }
  288. $parent = $this->getParent($id);
  289. if ($parent) {
  290. $nextDepth = $depth + 1;
  291. $array->prepend($parent); // 添加到开头
  292. $this->getAncestors($parent->{$this->getTreeNodeIdName()}, $nextDepth);
  293. }
  294. return $array;
  295. }
  296. }