SortableTraitPinned.php 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231
  1. <?php
  2. namespace App\Traits;
  3. use ArrayAccess;
  4. use Illuminate\Database\Eloquent\Builder;
  5. use Illuminate\Database\Eloquent\SoftDeletingScope;
  6. use Illuminate\Support\Facades\Event;
  7. use InvalidArgumentException;
  8. use Spatie\EloquentSortable\EloquentModelSortedEvent;
  9. use Spatie\EloquentSortable\Sortable;
  10. /*
  11. * 列表排序is_pinned desc,order desc时用这个trait
  12. */
  13. trait SortableTraitPinned
  14. {
  15. public static function bootSortableTrait()
  16. {
  17. static::creating(function ($model) {
  18. if ($model instanceof Sortable && $model->shouldSortWhenCreating()) {
  19. $model->setHighestOrderNumber();
  20. }
  21. });
  22. }
  23. public function setHighestOrderNumber(): void
  24. {
  25. $orderColumnName = $this->determineOrderColumnName();
  26. $this->$orderColumnName = $this->getHighestOrderNumber() + 1;
  27. }
  28. public function getHighestOrderNumber(): int
  29. {
  30. return (int)$this->buildSortQuery()->max($this->determineOrderColumnName());
  31. }
  32. public function getLowestOrderNumber(): int
  33. {
  34. return (int)$this->buildSortQuery()->min($this->determineOrderColumnName());
  35. }
  36. public function scopeOrdered(Builder $query, string $direction = 'asc')
  37. {
  38. return $query->orderBy($this->determineOrderColumnName(), $direction);
  39. }
  40. public static function setNewOrder(
  41. $ids,
  42. int $startOrder = 1,
  43. string $primaryKeyColumn = null,
  44. callable $modifyQuery = null
  45. ): void {
  46. if (! is_array($ids) && ! $ids instanceof ArrayAccess) {
  47. throw new InvalidArgumentException('You must pass an array or ArrayAccess object to setNewOrder');
  48. }
  49. $model = new static();
  50. $orderColumnName = $model->determineOrderColumnName();
  51. if (is_null($primaryKeyColumn)) {
  52. $primaryKeyColumn = $model->getQualifiedKeyName();
  53. }
  54. if (config('eloquent-sortable.ignore_timestamps', false)) {
  55. static::$ignoreTimestampsOn = array_values(array_merge(static::$ignoreTimestampsOn, [static::class]));
  56. }
  57. foreach ($ids as $id) {
  58. static::withoutGlobalScope(SoftDeletingScope::class)
  59. ->when(is_callable($modifyQuery), function ($query) use ($modifyQuery) {
  60. return $modifyQuery($query);
  61. })
  62. ->where($primaryKeyColumn, $id)
  63. ->update([$orderColumnName => $startOrder++]);
  64. }
  65. Event::dispatch(new EloquentModelSortedEvent(static::class));
  66. if (config('eloquent-sortable.ignore_timestamps', false)) {
  67. static::$ignoreTimestampsOn = array_values(array_diff(static::$ignoreTimestampsOn, [static::class]));
  68. }
  69. }
  70. public static function setNewOrderByCustomColumn(string $primaryKeyColumn, $ids, int $startOrder = 1)
  71. {
  72. self::setNewOrder($ids, $startOrder, $primaryKeyColumn);
  73. }
  74. public function determineOrderColumnName(): string
  75. {
  76. return $this->sortable['order_column_name'] ?? config('eloquent-sortable.order_column_name', 'order_column');
  77. }
  78. /**
  79. * Determine if the order column should be set when saving a new model instance.
  80. */
  81. public function shouldSortWhenCreating(): bool
  82. {
  83. return $this->sortable['sort_when_creating'] ?? config('eloquent-sortable.sort_when_creating', true);
  84. }
  85. /*
  86. * 由于排序在order desc时会出错,所以这里重写了 SortableTrait 中 moveOrderDown 和 moveOrderUp方法
  87. */
  88. public function moveOrderDown(): static
  89. {
  90. $orderColumnName = $this->determineOrderColumnName();
  91. $swapWithModel = $this->buildSortQuery()->limit(1)
  92. ->orderBy('is_pinned', 'desc')->orderBy('order', 'desc')
  93. ->where($orderColumnName, '<', $this->$orderColumnName)
  94. ->where('is_pinned', '=', $this->is_pinned)
  95. ->first();
  96. if (! $swapWithModel) {
  97. return $this;
  98. }
  99. if (($this->is_pinned == 1 && $swapWithModel->is_pinned == 0)
  100. || ($this->is_pinned == 0 && $swapWithModel->is_pinned == 1)) {
  101. throw new \Exception('No sorting allowed');
  102. }
  103. return $this->swapOrderWithModel($swapWithModel);
  104. }
  105. public function moveOrderUp(): static
  106. {
  107. $orderColumnName = $this->determineOrderColumnName();
  108. $swapWithModel = $this->buildSortQuery()->limit(1)
  109. ->orderBy('is_pinned', 'desc')->orderBy('order', 'asc')
  110. ->where($orderColumnName, '>', $this->$orderColumnName)
  111. ->where('is_pinned', '=', $this->is_pinned)
  112. ->first();
  113. if (! $swapWithModel) {
  114. return $this;
  115. }
  116. // var_dump($this->is_pinned, $swapWithModel->is_pinned);
  117. // var_dump($this->id, $swapWithModel->id);
  118. // exit;
  119. if (($this->is_pinned == 1 && $swapWithModel->is_pinned == 0)
  120. || ($this->is_pinned == 0 && $swapWithModel->is_pinned == 1)) {
  121. throw new \Exception('No sorting allowed');
  122. }
  123. return $this->swapOrderWithModel($swapWithModel);
  124. }
  125. public function swapOrderWithModel(Sortable $otherModel): static
  126. {
  127. $orderColumnName = $this->determineOrderColumnName();
  128. $oldOrderOfOtherModel = $otherModel->$orderColumnName;
  129. $otherModel->$orderColumnName = $this->$orderColumnName;
  130. $otherModel->save();
  131. $this->$orderColumnName = $oldOrderOfOtherModel;
  132. $this->save();
  133. return $this;
  134. }
  135. public static function swapOrder(Sortable $model, Sortable $otherModel): void
  136. {
  137. $model->swapOrderWithModel($otherModel);
  138. }
  139. public function moveToStart(): static
  140. {
  141. $firstModel = $this->buildSortQuery()->limit(1)
  142. ->ordered()
  143. ->first();
  144. if ($firstModel->getKey() === $this->getKey()) {
  145. return $this;
  146. }
  147. $orderColumnName = $this->determineOrderColumnName();
  148. $this->$orderColumnName = $firstModel->$orderColumnName;
  149. $this->save();
  150. $this->buildSortQuery()->where($this->getQualifiedKeyName(), '!=', $this->getKey())->increment(
  151. $orderColumnName
  152. );
  153. return $this;
  154. }
  155. public function moveToEnd(): static
  156. {
  157. $maxOrder = $this->getHighestOrderNumber();
  158. $orderColumnName = $this->determineOrderColumnName();
  159. if ($this->$orderColumnName === $maxOrder) {
  160. return $this;
  161. }
  162. $oldOrder = $this->$orderColumnName;
  163. $this->$orderColumnName = $maxOrder;
  164. $this->save();
  165. $this->buildSortQuery()->where($this->getQualifiedKeyName(), '!=', $this->getKey())
  166. ->where($orderColumnName, '>', $oldOrder)
  167. ->decrement($orderColumnName);
  168. return $this;
  169. }
  170. public function isLastInOrder(): bool
  171. {
  172. $orderColumnName = $this->determineOrderColumnName();
  173. return (int)$this->$orderColumnName === $this->getHighestOrderNumber();
  174. }
  175. public function isFirstInOrder(): bool
  176. {
  177. $orderColumnName = $this->determineOrderColumnName();
  178. return (int)$this->$orderColumnName === $this->getLowestOrderNumber();
  179. }
  180. public function buildSortQuery(): Builder
  181. {
  182. return static::query();
  183. }
  184. }