Total Complexity | 59 |
Total Lines | 238 |
Duplicated Lines | 0 % |
Changes | 2 | ||
Bugs | 0 | Features | 0 |
Complex classes like Filterable often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use Filterable, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | trait Filterable |
||
13 | { |
||
14 | /** |
||
15 | * |
||
16 | * @param $query |
||
17 | * @param array|null $filterAttributes |
||
18 | * @return mixed |
||
19 | */ |
||
20 | public function scopeFilterable($query, array $filterAttributes = null) |
||
21 | { |
||
22 | $filterable = $this->getFilterable(); |
||
23 | $attributes = $this->getFilterAttributes($filterAttributes); |
||
24 | |||
25 | if (! empty($attributes)) { |
||
26 | $query->where(function ($q) use ($attributes, $filterable) { |
||
27 | foreach ($filterable as $key => $filterType) { |
||
28 | switch ($filterType) { |
||
29 | case 'equal': |
||
30 | if (array_key_exists($key, $attributes) && $attributes[$key] !== null) { |
||
31 | $this->fWhereEqual($q, $key, $attributes); |
||
32 | } |
||
33 | break; |
||
34 | case 'like': |
||
35 | if (array_key_exists($key, $attributes) && $attributes[$key] !== null) { |
||
36 | $q->where($key, 'LIKE', '%' . $attributes[$key] . '%'); |
||
37 | } |
||
38 | break; |
||
39 | case 'in': |
||
40 | if (array_key_exists($key, $attributes) && $attributes[$key] !== null) { |
||
41 | $this->fWhereIn($q, $key, $attributes); |
||
42 | } |
||
43 | break; |
||
44 | case 'between': |
||
45 | $fromVal = $attributes[$key . '_from'] ?? null; |
||
46 | $toVal = $attributes[$key . '_to'] ?? null; |
||
47 | if ($fromVal != null && $toVal != null) { |
||
48 | $q->whereBetween($key, [$fromVal, $toVal]); |
||
49 | } elseif ($fromVal != null && $toVal == null) { |
||
50 | $q->where($key, '>=', $fromVal); |
||
51 | } elseif ($fromVal == null && $toVal != null) { |
||
52 | $q->where($key, '<=', $toVal); |
||
53 | } |
||
54 | break; |
||
55 | case 'equal_date': |
||
56 | if (array_key_exists($key, $attributes)) { |
||
57 | try { |
||
58 | $date = Carbon::parse($attributes[$key])->toDateString(); |
||
59 | $q->whereDate($key, $date); |
||
60 | } catch (\Exception $exception) { |
||
61 | \Log::error(__METHOD__ . $exception->getMessage()); |
||
62 | } |
||
63 | } |
||
64 | break; |
||
65 | case 'between_date': |
||
66 | $fromVal = $attributes[$key . '_from'] ?? null; |
||
67 | $toVal = $attributes[$key . '_to'] ?? null; |
||
68 | |||
69 | try { |
||
70 | if ($fromVal != null && $toVal != null) { |
||
71 | $dateFrom = Carbon::parse($fromVal); |
||
72 | $dateTo = Carbon::parse($toVal)->addDay()->addSecond(-1); |
||
73 | $q->whereBetween($key, [$dateFrom, $dateTo]); |
||
74 | } elseif ($fromVal != null && $toVal == null) { |
||
75 | $dateFrom = Carbon::parse($fromVal); |
||
76 | $q->where($key, '>=', $dateFrom); |
||
77 | } elseif ($fromVal == null && $toVal != null) { |
||
78 | $dateTo = Carbon::parse($toVal)->addDay()->addSecond(-1); |
||
79 | $q->where($key, '<=', $dateTo); |
||
80 | } |
||
81 | } catch (\Exception $exception) { |
||
82 | \Log::error(__METHOD__ . $exception->getMessage()); |
||
83 | } |
||
84 | break; |
||
85 | case 'custom': |
||
86 | $method = 'scopeCustomFilterable'; |
||
87 | if (array_key_exists($key, $attributes) && method_exists(self::class, $method)) { |
||
88 | $this->{$method}($q, $key, $attributes[$key]); |
||
89 | } |
||
90 | break; |
||
91 | } |
||
92 | } |
||
93 | }); |
||
94 | } |
||
95 | |||
96 | return $query; |
||
97 | } |
||
98 | |||
99 | protected function customFilterable($q) |
||
101 | //... |
||
102 | } |
||
103 | |||
104 | protected function getFilterable(): array |
||
105 | { |
||
106 | return $this->filterable ?? []; |
||
107 | } |
||
108 | |||
109 | protected function getFilterAttributes(array $attributes = null) |
||
110 | { |
||
111 | $requestKeyName = config('filterable.input_keys.filter', 'filter'); |
||
112 | |||
113 | if (! empty($attributes)) { |
||
114 | return $attributes; |
||
115 | } elseif ($requestKeyName && request($requestKeyName) && is_array(request($requestKeyName))) { |
||
116 | return request($requestKeyName); |
||
117 | } |
||
118 | |||
119 | return []; |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * https://site.test/post?filter[status]=post_published |
||
124 | * @param $q |
||
125 | * @param $key |
||
126 | * @param array $attributes |
||
127 | * @return mixed |
||
128 | */ |
||
129 | protected function fWhereEqual($q, $key, array $attributes) |
||
145 | } |
||
146 | |||
147 | /** |
||
148 | * https://site.test/post?filter[status][]=post_moderation&filter[status][]=post_published |
||
149 | * or if set $filterSeparator |
||
150 | * https://site.test/post?filter[status]=post_moderation|post_published |
||
151 | * or |
||
152 | * https://site.test/post?filter[status]=post_moderation |
||
153 | * @param $q |
||
154 | * @param $key |
||
155 | * @param array $attributes |
||
156 | * @return mixed |
||
157 | */ |
||
158 | protected function fWhereIn($q, $key, array $attributes) |
||
159 | { |
||
160 | //$value = is_array($attributes[$key]) ? $attributes[$key] : [$attributes[$key]]; |
||
161 | |||
162 | if (is_array($attributes[$key])) { |
||
163 | $value = $attributes[$key]; |
||
164 | } elseif ($filterSeparator = '|') { |
||
165 | $value = explode($filterSeparator, $attributes[$key]); |
||
166 | } else { |
||
167 | $value = [$attributes[$key]]; |
||
168 | } |
||
169 | |||
170 | if (preg_match('/\./', $key)) { |
||
171 | $has = preg_replace("/\.\w+$/", '', $key); |
||
172 | $key = preg_replace("/\w+\./", '', $key); |
||
173 | |||
174 | $q->whereHas($has, function ($qq) use ($key, $value) { |
||
175 | $qq->whereIn($key, $value); |
||
176 | }); |
||
177 | } else { |
||
178 | $q->whereIn($key, $value); |
||
179 | } |
||
180 | |||
181 | return $q; |
||
182 | } |
||
183 | |||
184 | /** |
||
185 | * https://site.test/post?q=запрос |
||
186 | * |
||
187 | * @param \Illuminate\Database\Eloquent\Builder $query |
||
188 | * @param string|null $search |
||
189 | */ |
||
190 | public function scopeSearchable(Builder $query, string $search = null) |
||
191 | { |
||
192 | $search = $this->getSearchStr($search); |
||
193 | |||
194 | $query->when($search !== null, function () use ($query, $search) { |
||
195 | $query->where(function ($query2) use ($search) { |
||
196 | foreach ($this->getSearchable() as $key) { |
||
197 | if (preg_match('/\./', $key)) { |
||
198 | $has = preg_replace("/\.\w+$/", '', $key); |
||
199 | $key = preg_replace("/\w+\./", '', $key); |
||
200 | $query2->orWhereHas($has, function ($qq) use ($key, $search) { |
||
201 | $qq->where($key, 'LIKE', "%$search%"); |
||
202 | }); |
||
203 | } else { |
||
204 | $query2->orWhere($key, 'LIKE', "%$search%"); |
||
205 | } |
||
206 | } |
||
207 | }); |
||
208 | }); |
||
209 | } |
||
210 | |||
211 | /** |
||
212 | * @return array |
||
213 | */ |
||
214 | public function getSearchable(): array |
||
215 | { |
||
216 | return $this->searchable ?? []; |
||
217 | } |
||
218 | |||
219 | /** |
||
220 | * @param string|null $search |
||
221 | * @return array|\Illuminate\Http\Request|null|string |
||
222 | */ |
||
223 | protected function getSearchStr(string $search = null) |
||
234 | } |
||
235 | |||
236 | /** |
||
237 | * @return bool |
||
238 | */ |
||
239 | public static function isFilterable(): bool |
||
240 | { |
||
242 | } |
||
243 | |||
244 | /** |
||
245 | * @return bool |
||
246 | */ |
||
247 | public static function isSearchable(): bool |
||
250 | } |
||
251 | } |
The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g.
excluded_paths: ["lib/*"]
, you can move it to the dependency path list as follows:For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths