Complex classes like Searchzy 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
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 Searchzy, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
8 | trait Searchzy { |
||
9 | |||
10 | /** |
||
11 | * Agrupa todas los closures de las relaciones del Modelo, en forma de árbol. |
||
12 | * |
||
13 | * @var array |
||
14 | */ |
||
15 | protected $relationConstraints = []; |
||
16 | |||
17 | /** |
||
18 | * Define el array de las relaciones y el query de las relaciones. En el query |
||
19 | * ya se aplicaron las closures de cada relación. |
||
20 | * |
||
21 | * @var array |
||
22 | */ |
||
23 | protected $eagerRelationConstraints = []; |
||
24 | |||
25 | /** |
||
26 | * Define el array con todos los inputs searchable del Modelo. |
||
27 | * |
||
28 | * @var array |
||
29 | */ |
||
30 | protected $searchableInputs = []; |
||
31 | |||
32 | /** |
||
33 | * Define el array con todos los inputs filterable del Modelo. |
||
34 | * |
||
35 | * @var array |
||
36 | */ |
||
37 | protected $filterableInputs = []; |
||
38 | |||
39 | /** |
||
40 | * Define el valor de la 'keyword' de searchzy. |
||
41 | * |
||
42 | * @var array |
||
43 | */ |
||
44 | protected $searchableKeyword; |
||
45 | |||
46 | /** |
||
47 | * Scope que realiza una búsqueda searchzy. |
||
48 | * |
||
49 | * @param Illuminate\Database\Eloquent\Builder $query |
||
50 | * @return Illuminate\Database\Eloquent\Builder |
||
51 | */ |
||
52 | public function scopeSearchzy($query, $keyword = NULL){ |
||
53 | |||
54 | $keyword = $keyword ?: config('searchzy.keyword'); |
||
55 | |||
56 | $this->searchableKeyword = request()->get($keyword, NULL); |
||
57 | |||
58 | $this->searchableInputsKeyword = $this->getInputsKeyword(); |
||
|
|||
59 | |||
60 | $this->filterableInputs = $this->getInputsRequest('filterable'); |
||
61 | |||
62 | $this->searchableInputs = $this->getInputsRequest('searchable'); |
||
63 | |||
64 | $query = $this->parseInputsKeywordConstraints($query); |
||
65 | |||
66 | $query = $this->parseRelationConstraints($query); |
||
67 | |||
68 | $query = $this->loadRelationContraints($query); |
||
69 | |||
70 | return $query; |
||
71 | } |
||
72 | |||
73 | /** |
||
74 | * Agrupa las relaciones del Modelo. Retorna un array 'arbol' de las relaciones y sus columnas. |
||
75 | * |
||
76 | * @param array $arrInputs |
||
77 | * @return array |
||
78 | */ |
||
79 | protected function parseRelationInputs($arrInputs){ |
||
80 | |||
81 | $relationInputs = []; |
||
82 | |||
83 | foreach (array_keys($arrInputs) as $attribute) { |
||
84 | |||
85 | if (Str::contains($attribute, ':')) { |
||
86 | |||
87 | [$relation, $column] = explode(':', $attribute); |
||
88 | |||
89 | $relationInputs[$relation][] = $column; |
||
90 | } |
||
91 | } |
||
92 | |||
93 | return $relationInputs; |
||
94 | } |
||
95 | |||
96 | /** |
||
97 | * Agrupas las columnas propias del Modelo. |
||
98 | * |
||
99 | * @param array $arrInputs |
||
100 | * @return array |
||
101 | */ |
||
102 | protected function parseModelInputs($arrInputs){ |
||
103 | |||
104 | $modelInputs = []; |
||
105 | |||
106 | foreach (array_keys($arrInputs) as $attribute) { |
||
107 | |||
108 | if (!Str::contains($attribute, ':')) { |
||
109 | |||
110 | $modelInputs[] = $attribute; |
||
111 | } |
||
112 | } |
||
113 | |||
114 | return $modelInputs; |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Parsea los inputs de búsqueda del Modelo. |
||
119 | * |
||
120 | * @param Builder $query |
||
121 | * @return Builder |
||
122 | */ |
||
123 | protected function parseInputsKeywordConstraints($query){ |
||
124 | |||
125 | //Aplicación del los where's de los atributos searchable propios del Modelo. |
||
126 | |||
127 | $searchableModelInputs = $this->parseModelInputs($this->searchableInputsKeyword); |
||
128 | |||
129 | $query = $query->where(function($query) use ($searchableModelInputs){ |
||
130 | |||
131 | // Aplicación de los where's en las columnas propias del Modelo. |
||
132 | |||
133 | $query = $query->where(function($query) use ($searchableModelInputs){ |
||
134 | |||
135 | foreach ($searchableModelInputs as $attribute) { |
||
136 | |||
137 | $value = Arr::get($this->searchableInputsKeyword, $attribute, $this->searchableKeyword); |
||
138 | |||
139 | if ($value) { |
||
140 | |||
141 | $query->orWhere($attribute, 'LIKE', "%{$value}") |
||
142 | ->orWhere($attribute, 'LIKE', "{$value}%") |
||
143 | ->orWhere($attribute, 'LIKE', "%{$value}%"); |
||
144 | } |
||
145 | } |
||
146 | }); |
||
147 | |||
148 | // Aplicación de los where's de las relaciones del Modelo que cuyo valor es el del 'keyword'. |
||
149 | |||
150 | if ($this->searchableKeyword) { |
||
151 | |||
152 | $searchableRelationInputs = $this->parseRelationInputs($this->searchableInputsKeyword); |
||
153 | |||
154 | $searchableRelation = $this->parseRelationInputs($this->searchableInputs); |
||
155 | |||
156 | foreach ($searchableRelationInputs as $attribute => $columns) { |
||
157 | |||
158 | if (!in_array($attribute, array_keys($searchableRelation))) { |
||
159 | |||
160 | $query->orWhereHas($attribute, function ($query) use ($attribute, $searchableRelationInputs) { |
||
161 | |||
162 | $query = $query->where(function($query) use ($attribute, $searchableRelationInputs){ |
||
163 | |||
164 | $columns = $searchableRelationInputs[$attribute] ?? []; |
||
165 | |||
166 | foreach ($columns as $column) { |
||
167 | |||
168 | $value = $this->searchableInputsKeyword["{$attribute}:{$column}"] ?? $this->searchableKeyword; |
||
169 | |||
170 | $query->orWhere($column, 'LIKE', "%{$value}") |
||
171 | ->orWhere($column, 'LIKE', "{$value}%") |
||
172 | ->orWhere($column, 'LIKE', "%{$value}%"); |
||
173 | } |
||
174 | }); |
||
175 | }); |
||
176 | } |
||
177 | } |
||
178 | } |
||
179 | }); |
||
180 | |||
181 | // Aplicación del los where's de los atributos filterable propios del Modelo. |
||
182 | |||
183 | $filterableModelInputs = $this->parseModelInputs($this->filterableInputs); |
||
184 | |||
185 | $filterableModelInputs = Arr::only($this->filterableInputs, $filterableModelInputs); |
||
186 | |||
187 | foreach ($filterableModelInputs as $column => $value) { |
||
188 | |||
189 | $query->where($column, '=', str_trimmer($value)); |
||
190 | } |
||
191 | |||
192 | // Se añade los constraints para las relaciones definidads en el searchable del Modelo. |
||
193 | |||
194 | $searchableRelationInputs = $this->parseRelationInputs($this->searchableInputs); |
||
195 | |||
196 | foreach ($searchableRelationInputs as $attribute => $columns) { |
||
197 | |||
198 | $this->addRelationConstraints([$attribute => function ($query) use ($attribute, $searchableRelationInputs) { |
||
199 | |||
200 | $columns = $searchableRelationInputs[$attribute] ?? []; |
||
201 | |||
202 | foreach ($columns as $column) { |
||
203 | |||
204 | $value = Arr::get($this->searchableInputs, "{$attribute}:{$column}"); |
||
205 | |||
206 | $query->where(function($query) use ($column, $value){ |
||
207 | |||
208 | $query->orWhere($column, 'LIKE', "%{$value}") |
||
209 | ->orWhere($column, 'LIKE', "{$value}%") |
||
210 | ->orWhere($column, 'LIKE', "%{$value}%"); |
||
211 | }); |
||
212 | } |
||
213 | }]); |
||
214 | } |
||
215 | |||
216 | // Se añade los constraints de las relaciones definidads en el filterable del Modelo. |
||
217 | |||
218 | $filterableRelationInputs = $this->parseRelationInputs($this->filterableInputs); |
||
219 | |||
220 | foreach ($filterableRelationInputs as $attribute => $columns) { |
||
221 | |||
222 | $this->addRelationConstraints([$attribute => function ($query) use ($attribute, $filterableRelationInputs) { |
||
223 | |||
224 | $columns = $filterableRelationInputs[$attribute] ?? []; |
||
225 | |||
226 | foreach ($columns as $column) { |
||
227 | |||
228 | $value = Arr::get($this->filterableInputs, "{$attribute}:{$column}"); |
||
229 | |||
230 | $query->where($column, '=', str_trimmer($value)); |
||
231 | } |
||
232 | }]); |
||
233 | } |
||
234 | |||
235 | return $query; |
||
236 | } |
||
237 | |||
238 | /** |
||
239 | * Agrupa las closures por cada relación en {relationConstraints}. |
||
240 | * |
||
241 | * @param array $relations |
||
242 | * @return void |
||
243 | */ |
||
244 | protected function addRelationConstraints(array $relations){ |
||
245 | |||
246 | foreach ($relations as $name => $closure) { |
||
247 | |||
248 | $this->relationConstraints[$name][] = $closure; |
||
249 | } |
||
250 | } |
||
251 | |||
252 | /** |
||
253 | * Sí hay closures en las relaciones, aplica al query y agrupalas |
||
254 | * por cada la relación en {eagerRelationConstraints}. |
||
255 | * |
||
256 | * @param Builder $query |
||
257 | * @return Builder |
||
258 | */ |
||
259 | protected function parseRelationConstraints($query){ |
||
277 | |||
278 | /** |
||
279 | * Aplica los 'closures' que estan en {eagerRelationConstraints} |
||
280 | * por cada relación vía whereHas. |
||
281 | * |
||
282 | * @param Builder $query |
||
283 | * @return Builder |
||
284 | */ |
||
285 | protected function loadRelationContraints($query){ |
||
297 | |||
298 | /** |
||
299 | * Retorna un array con los inputs 'searchables' cuyo valor |
||
300 | * será el que ingresado en la 'keyword'. |
||
301 | * |
||
302 | * @param string $keyword |
||
303 | * @return array |
||
304 | */ |
||
305 | protected function getInputsKeyword(){ |
||
322 | |||
323 | /** |
||
324 | * Obtiene los inputs definidos en el Modelo de las propiedades 'searchable' y 'filterable'. |
||
325 | * |
||
326 | * @param string $property |
||
327 | * @return array |
||
328 | */ |
||
329 | protected function getInputsRequest($property){ |
||
346 | |||
347 | /** |
||
348 | * Obtiene los inputs definidos en el Modelo que estan en el Request. |
||
349 | * |
||
350 | * @link (https://timacdonald.me/query-scopes-meet-action-scopes/) |
||
351 | * @param Builder $query |
||
352 | * @param array $extraParams |
||
353 | * @param mixed $default |
||
354 | * @return array |
||
355 | */ |
||
356 | public function scopeSearchzyInputs($query, $extraParams = [], $default = '') : array { |
||
376 | |||
377 | } |
||
378 |
An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.
If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.