Filterable.php 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. namespace App\Traits;
  3. use Carbon\Carbon;
  4. use Illuminate\Database\Eloquent\Builder;
  5. use Illuminate\Support\Str;
  6. trait Filterable
  7. {
  8. /**
  9. * 通用筛选方法(支持多场景)
  10. *
  11. * @param Builder $query
  12. * @param array $filters = [
  13. * 'field' => value, // 直接查询(=)
  14. * 'scope:method' => value, // 调用模型 scope 方法
  15. * 'relation.field' => value, // 关联模型字段查询
  16. * 'date_range:field' => [ // 日期范围
  17. * 'start' => value1,
  18. * 'end' => value2,
  19. * ],
  20. * 'or' => [ // OR 条件组
  21. * ['field1' => value1],
  22. * ['field2' => value2]
  23. * ]
  24. * ]
  25. * @param array $options = [
  26. * 'search' => value, // 搜索词
  27. * 'searchable' => [ // 可搜索字段(支持关联字段)
  28. * 'field', // 当前模型字段搜索
  29. * 'relation.field', // 关联模型字段搜索
  30. * ],
  31. * 'sort_by' => 'field', // 排序字段
  32. * 'sort_order' => 'asc/desc' // 排序方向(asc:升序、desc:降序)
  33. * ]
  34. * @return Builder
  35. */
  36. public function scopeApplyFilters(Builder $query, array $filters = [], array $options = []): Builder
  37. {
  38. // 处理筛选条件
  39. foreach ($filters as $field => $value) {
  40. // 跳过空值
  41. if (is_null($value)) continue;
  42. // 动态解析筛选类型
  43. switch (true) {
  44. case Str::startsWith($field, 'scope:'):
  45. $this->applyScopeFilter($query, $field, $value);
  46. break;
  47. case Str::contains($field, '.'):
  48. $this->applyRelationFilter($query, $field, $value);
  49. break;
  50. case Str::startsWith($field, 'date_range:'):
  51. $this->applyDateRangeFilter($query, $field, $value);
  52. break;
  53. case $field === 'or':
  54. $this->applyOrConditions($query, $value);
  55. break;
  56. default:
  57. $this->applyDefaultFilter($query, $field, $value);
  58. }
  59. }
  60. // 处理全局搜索
  61. if (!empty($options['search']) && !empty($options['searchable'])) {
  62. $this->applyGlobalSearch($query, $options['search'], $options['searchable']);
  63. }
  64. // 处理排序
  65. $this->applySorting($query, $options);
  66. return $query;
  67. }
  68. /**
  69. * 应用 Scope 方法筛选
  70. */
  71. protected function applyScopeFilter(Builder $query, string $field, $value): void
  72. {
  73. $scopeMethod = Str::after($field, 'scope:');
  74. if (method_exists($this, 'scope' . Str::studly($scopeMethod))) {
  75. $query->{$scopeMethod}($value);
  76. } else {
  77. // 记录未知 Scope 方法
  78. logger()->warning("Undefined scope method: {$scopeMethod}");
  79. }
  80. }
  81. /**
  82. * 应用关联模型筛选
  83. */
  84. protected function applyRelationFilter(Builder $query, string $field, $value): void
  85. {
  86. [$relation, $relationField] = explode('.', $field, 2);
  87. $query->whereHas($relation, function ($q) use ($relationField, $value) {
  88. $q->where($relationField, $value);
  89. });
  90. }
  91. /**
  92. * 应用日期范围筛选
  93. */
  94. protected function applyDateRangeFilter(Builder $query, string $field, array $value): void
  95. {
  96. $dataRangeField = Str::after($field, 'date_range:');
  97. if (isset($value['start']) && isset($value['end'])) {
  98. $start = Carbon::parse($value['start'])->startOfDay();
  99. $end = Carbon::parse($value['end'])->endOfDay();
  100. $query->whereBetween($dataRangeField, [$start, $end]);
  101. }
  102. }
  103. /**
  104. * 应用 OR 条件组
  105. */
  106. protected function applyOrConditions(Builder $query, array $conditions): void
  107. {
  108. $query->where(function ($q) use ($conditions) {
  109. foreach ($conditions as $group) {
  110. $q->orWhere(function ($subQ) use ($group) {
  111. foreach ($group as $field => $value) {
  112. $subQ->where($field, $value);
  113. }
  114. });
  115. }
  116. });
  117. }
  118. /**
  119. * 应用默认筛选(= 条件)
  120. */
  121. protected function applyDefaultFilter(Builder $query, string $field, $value): void
  122. {
  123. $query->where($field, $value);
  124. }
  125. /**
  126. * 全局模糊搜索
  127. */
  128. protected function applyGlobalSearch(Builder $query, string $searchTerm, array $searchableFields): void
  129. {
  130. $query->where(function ($q) use ($searchTerm, $searchableFields) {
  131. foreach ($searchableFields as $field) {
  132. if (Str::contains($field, '.')) {
  133. $this->applyRelationSearch($q, $field, $searchTerm);
  134. } else {
  135. $q->orWhere($field, 'LIKE', "%{$searchTerm}%");
  136. }
  137. }
  138. });
  139. }
  140. /**
  141. * 处理关联字段搜索
  142. */
  143. protected function applyRelationSearch(Builder $query, string $field, string $searchTerm): void
  144. {
  145. $relations = explode('.', $field);
  146. $column = array_pop($relations);
  147. $relation = implode('.', $relations);
  148. $query->orWhereHas($relation, function ($q) use ($column, $searchTerm) {
  149. $q->where($column, 'LIKE', "%{$searchTerm}%");
  150. });
  151. }
  152. /**
  153. * 应用排序逻辑
  154. */
  155. protected function applySorting(Builder $query, array $options): void
  156. {
  157. $sortBy = $options['sort_by'] ?? 'id';
  158. $sortOrder = $options['sort_order'] ?? 'desc';
  159. $query->orderBy($sortBy, $sortOrder);
  160. }
  161. }