Completed
Push — master ( 7acd91...c1bd00 )
by Jhorman Alexander
07:52 queued 06:31
created

Searchzy::parseModelInputs()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
nc 3
nop 1
dl 0
loc 14
rs 9.7998
c 0
b 0
f 0
1
<?php
2
3
namespace Jhormantasayco\LaravelSearchzy;
4
5
use Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
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();
0 ignored issues
show
Bug introduced by
The property searchableInputsKeyword does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The variable $relation does not exist. Did you mean $relationInputs?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
Bug introduced by
The variable $column does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
88
89
                $relationInputs[$relation][] = $column;
0 ignored issues
show
Bug introduced by
The variable $relation does not exist. Did you mean $relationInputs?

This check looks for variables that are accessed but have not been defined. It raises an issue if it finds another variable that has a similar name.

The variable may have been renamed without also renaming all references.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The property searchableInputsKeyword does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The property searchableInputsKeyword does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
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);
0 ignored issues
show
Bug introduced by
The property searchableInputsKeyword does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
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){
0 ignored issues
show
Unused Code introduced by
$query is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
163
164
                                $columns = $searchableRelationInputs[$attribute] ?? [];
165
166
                                foreach ($columns as $column) {
167
168
                                    $value = $this->searchableInputsKeyword["{$attribute}:{$column}"] ?? $this->searchableKeyword;
0 ignored issues
show
Bug introduced by
The property searchableInputsKeyword does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
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){
260
261
        if ($this->relationConstraints) {
262
263
            foreach ($this->relationConstraints as $relation => $constraints) {
264
265
                $this->eagerRelationConstraints[$relation] = function($query) use ($constraints) {
266
267
                    foreach ($constraints as $constraint) {
268
269
                        $constraint($query);
270
                    }
271
                };
272
            }
273
        }
274
275
        return $query;
276
    }
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){
286
287
        if ($this->eagerRelationConstraints) {
288
289
            foreach ($this->eagerRelationConstraints as $relation => $closure) {
290
291
                $query->whereHas($relation, $closure);
292
            }
293
        }
294
295
        return $query;
296
    }
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
0 ignored issues
show
Bug introduced by
There is no parameter named $keyword. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
303
     * @return array
304
     */
305
    protected function getInputsKeyword(){
306
307
        $arrInputs  = [];
308
309
        $searchable = Arr::wrap($this->searchable);
0 ignored issues
show
Bug introduced by
The property searchable does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
310
311
        $searchable = Arr::isAssoc($searchable) ? array_keys($searchable) : $searchable;
312
313
        foreach ($searchable as $column) {
314
315
            $arrInputs[$column] = $this->searchableKeyword ?: request()->get($column, NULL);
316
        }
317
318
        $arrInputs = array_keys_replace($arrInputs, Arr::wrap($this->searchable));
0 ignored issues
show
Bug introduced by
The property searchable does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
319
320
        return $arrInputs;
321
    }
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){
330
331
        $arrInputs = [];
332
333
        if (property_exists($this, $property)) {
334
335
            $arrInputs = Arr::wrap($this->{$property});
336
337
            $arrInputs = Arr::isAssoc($arrInputs) ? array_keys($arrInputs) : $arrInputs;
338
339
            $arrInputs = array_filter_empty(request()->only($arrInputs));
340
341
            $arrInputs = array_keys_replace($arrInputs, Arr::wrap($this->{$property}));
342
        }
343
344
        return $arrInputs;
345
    }
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 {
0 ignored issues
show
Unused Code introduced by
The parameter $query is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
357
358
        $searchable = property_exists($this, 'searchable') ? Arr::wrap($this->searchable) : [];
0 ignored issues
show
Bug introduced by
The property searchable does not seem to exist. Did you mean searchableInputs?

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.

Loading history...
359
360
        $searchable = Arr::isAssoc($searchable) ? array_keys($searchable) : $searchable;
361
362
        $filterable = property_exists($this, 'filterable') ? Arr::wrap($this->filterable) : [];
0 ignored issues
show
Bug introduced by
The property filterable does not seem to exist. Did you mean filterableInputs?

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.

Loading history...
363
364
        $filterable = Arr::isAssoc($filterable) ? array_keys($filterable) : $filterable;
365
366
        $extraParams = Arr::wrap($extraParams);
367
368
        $keyword = Arr::wrap(config('searchzy.keyword'));
369
370
        $params  = array_merge($searchable, $filterable, $keyword, $extraParams);
371
372
        $params  = array_only_filler(request()->all(), $params, $default);
373
374
        return $params;
375
    }
376
377
}
378