HasTree.php 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. <?php
  2. namespace Dcat\Admin\Grid\Concerns;
  3. use Dcat\Admin\Admin;
  4. use Dcat\Admin\Grid\Events\Fetched;
  5. use Dcat\Admin\Grid\Events\Fetching;
  6. use Dcat\Admin\Repositories\EloquentRepository;
  7. use Dcat\Admin\Support\Helper;
  8. use Illuminate\Support\Collection;
  9. /**
  10. * Trait HasTree.
  11. *
  12. *
  13. * @method \Dcat\Admin\Grid grid()
  14. */
  15. trait HasTree
  16. {
  17. /**
  18. * @var string
  19. */
  20. protected $parentIdQueryName = '_parent_id_';
  21. /**
  22. * @var string
  23. */
  24. protected $depthQueryName = '_depth_';
  25. /**
  26. * @var bool
  27. */
  28. protected $showAllChildrenNodes = false;
  29. /**
  30. * @var bool
  31. */
  32. protected $allowedTreeQuery = true;
  33. /**
  34. * @var array
  35. */
  36. protected $treeIgnoreQueryNames = [];
  37. /**
  38. * @var mixed
  39. */
  40. protected $defaultParentId;
  41. /**
  42. * 开启树形表格功能.
  43. *
  44. * @param bool $showAll
  45. * @param bool $sortable
  46. * @param mixed $defaultParentId
  47. * @return void
  48. */
  49. public function enableTree(bool $showAll, bool $sortable, $defaultParentId = null)
  50. {
  51. $this->showAllChildrenNodes = $showAll;
  52. $this->defaultParentId = $defaultParentId;
  53. $this->grid()->listen(Fetching::class, function () use ($sortable) {
  54. $this->sortTree($sortable);
  55. $this->bindChildrenNodesQuery();
  56. if ($this->getParentIdFromRequest()) {
  57. $this->setPageName(
  58. $this->getChildrenPageName($this->getParentIdFromRequest())
  59. );
  60. }
  61. $this->addIgnoreQueries();
  62. });
  63. $this->grid()->listen(Fetched::class, function ($grid, Collection $collection) {
  64. if (! $this->getParentIdFromRequest()) {
  65. return;
  66. }
  67. if ($collection->isEmpty()) {
  68. return $grid->show(false);
  69. }
  70. $this->buildChildrenNodesPagination();
  71. });
  72. }
  73. /**
  74. * 设置保存为"前一个页面地址"时需要忽略的参数.
  75. */
  76. protected function addIgnoreQueries()
  77. {
  78. Admin::addIgnoreQueryName([
  79. $this->getParentIdQueryName(),
  80. $this->getDepthQueryName(),
  81. $this->getChildrenPageName($this->getParentIdFromRequest()),
  82. ]);
  83. }
  84. /**
  85. * 禁止树形表格查询.
  86. *
  87. * @return $this
  88. */
  89. public function disableBindTreeQuery()
  90. {
  91. $this->allowedTreeQuery = false;
  92. return $this->filterQueryBy(function ($query) {
  93. if (
  94. $query['method'] === 'where'
  95. && $query['arguments']
  96. && $query['arguments'][0] === optional($this->repository)->getParentColumn()
  97. ) {
  98. return false;
  99. }
  100. return true;
  101. });
  102. }
  103. /**
  104. * 设置子节点查询链接需要忽略的字段.
  105. *
  106. * @param string|array $keys
  107. * @return $this
  108. */
  109. public function treeUrlWithoutQuery($keys)
  110. {
  111. $this->treeIgnoreQueryNames = array_merge(
  112. $this->treeIgnoreQueryNames,
  113. (array) $keys
  114. );
  115. return $this;
  116. }
  117. public function generateTreeUrl()
  118. {
  119. return Helper::urlWithoutQuery(
  120. $this->grid()->filter()->urlWithoutFilters(),
  121. $this->treeIgnoreQueryNames
  122. );
  123. }
  124. protected function buildChildrenNodesPagination()
  125. {
  126. if ($this->grid()->allowPagination()) {
  127. $nextPage = $this->getCurrentChildrenPage() + 1;
  128. Admin::html(
  129. <<<HTML
  130. <next-page class="hidden">{$nextPage}</next-page>
  131. <last-page class="hidden">{$this->paginator()->lastPage()}</last-page>
  132. HTML
  133. );
  134. }
  135. }
  136. protected function sortTree(bool $sortable)
  137. {
  138. if (
  139. $sortable
  140. && $this->findQueryByMethod('orderBy')->isEmpty()
  141. && $this->findQueryByMethod('orderByDesc')->isEmpty()
  142. && ($orderColumn = $this->repository->getOrderColumn())
  143. ) {
  144. $this->orderBy($orderColumn)
  145. ->orderBy($this->repository->getKeyName());
  146. }
  147. }
  148. protected function bindChildrenNodesQuery()
  149. {
  150. if (! $this->allowedTreeQuery) {
  151. return;
  152. }
  153. $this->where($this->repository->getParentColumn(), $this->getParentIdFromRequest());
  154. }
  155. /**
  156. * @return mixed
  157. */
  158. public function getChildrenQueryNamePrefix()
  159. {
  160. return $this->grid->getName();
  161. }
  162. /**
  163. * @param mixed $parentId
  164. * @return string
  165. */
  166. public function getChildrenPageName($parentId)
  167. {
  168. return $this->getChildrenQueryNamePrefix().'_children_page_'.$parentId;
  169. }
  170. /**
  171. * @return int
  172. */
  173. public function getCurrentChildrenPage()
  174. {
  175. return $this->request->get(
  176. $this->getChildrenPageName(
  177. $this->getParentIdFromRequest()
  178. )
  179. ) ?: 1;
  180. }
  181. /**
  182. * @return string
  183. */
  184. public function getParentIdQueryName()
  185. {
  186. return $this->getChildrenQueryNamePrefix().$this->parentIdQueryName;
  187. }
  188. /**
  189. * @return int
  190. */
  191. public function getParentIdFromRequest()
  192. {
  193. return $this->request->get(
  194. $this->getParentIdQueryName()
  195. ) ?: $this->getDefaultParentId();
  196. }
  197. /**
  198. * 移除树相关参数.
  199. *
  200. * @param string $url
  201. * @return string
  202. */
  203. public function withoutTreeQuery($url)
  204. {
  205. if (! $url) {
  206. return $url;
  207. }
  208. parse_str(explode('?', $url)[1] ?? '', $originalQuery);
  209. $parentId = $originalQuery[$this->getParentIdQueryName()] ?? 0;
  210. if (! $parentId) {
  211. return $url;
  212. }
  213. return Helper::urlWithoutQuery($url, [
  214. $this->getParentIdQueryName(),
  215. $this->getChildrenPageName($parentId),
  216. $this->getDepthQueryName(),
  217. ]);
  218. }
  219. /**
  220. * 获取默认parent_id字段值.
  221. *
  222. * @return int|mixed
  223. */
  224. public function getDefaultParentId()
  225. {
  226. if ($this->defaultParentId !== null) {
  227. return $this->defaultParentId;
  228. }
  229. $repository = $this->grid->model()->repository();
  230. if ($repository instanceof EloquentRepository) {
  231. return $repository->model()->getDefaultParentId();
  232. }
  233. return 0;
  234. }
  235. /**
  236. * @return string
  237. */
  238. public function getDepthQueryName()
  239. {
  240. return $this->getChildrenQueryNamePrefix().$this->depthQueryName;
  241. }
  242. /**
  243. * @return int
  244. */
  245. public function getDepthFromRequest()
  246. {
  247. return $this->request->get(
  248. $this->getDepthQueryName()
  249. ) ?: 0;
  250. }
  251. /**
  252. * @return bool
  253. */
  254. public function showAllChildrenNodes()
  255. {
  256. return $this->showAllChildrenNodes;
  257. }
  258. }