Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

CrudFilter   F
last analyzed

Complexity

Total Complexity 65

Size/Duplication

Total Lines 608
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 143
dl 0
loc 608
rs 3.2
c 0
b 0
f 0
wmc 65

39 Methods

Rating   Name   Duplication   Size   Complexity  
A whenActive() 0 3 1
A remove() 0 3 1
A __call() 0 5 1
A view() 0 5 1
A applied() 0 5 1
A else() 0 3 1
A ifActive() 0 3 1
A after() 0 5 1
A removeFilterAttribute() 0 7 1
A label() 0 5 1
A options() 0 3 1
A logic() 0 5 1
A setAllOptionsValues() 0 3 1
A ifNotActive() 0 3 1
B applyDefaultLogic() 0 44 11
A makeFirst() 0 5 1
A dd() 0 5 1
A getViewWithNamespace() 0 3 1
A save() 0 11 2
A whenInactive() 0 3 1
A fallbackLogic() 0 5 1
A viewNamespace() 0 5 1
A makeLast() 0 6 1
A isActive() 0 7 2
A type() 0 6 1
A ifInactive() 0 3 1
A wasApplied() 0 3 1
A values() 0 5 3
A apply() 0 25 5
A forget() 0 13 3
A setOptionValue() 0 3 1
A whenNotActive() 0 3 1
A dump() 0 5 1
A getNamespacedViewWithFallbacks() 0 12 2
A wasNotApplied() 0 3 1
A crud() 0 3 1
A before() 0 5 1
B __construct() 0 29 6
A name() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like CrudFilter 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 CrudFilter, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel;
4
5
use Backpack\CRUD\app\Exceptions\BackpackProRequiredException;
6
use Backpack\CRUD\ViewNamespaces;
7
use Closure;
8
use Illuminate\Support\Str;
9
use Illuminate\Support\Traits\Conditionable;
10
use Symfony\Component\HttpFoundation\ParameterBag;
11
12
class CrudFilter
13
{
14
    use Conditionable;
15
16
    public $name; // the name of the filtered variable (db column name)
17
18
    public $type = 'select2'; // the name of the filter view that will be loaded
19
20
    public $key; //camelCased version of filter name to use in internal ids, js functions and css classes.
21
22
    public $label;
23
24
    public $placeholder;
25
26
    public $values;
27
28
    public $options;
29
30
    public $logic;
31
32
    public $fallbackLogic;
33
34
    public $currentValue;
35
36
    public $view;
37
38
    public $viewNamespace = 'crud::filters';
39
40
    public $applied = false;
41
42
    public function __construct($options, $values, $logic, $fallbackLogic)
43
    {
44
        if (! backpack_pro()) {
45
            throw new BackpackProRequiredException('Filter');
46
        }
47
        // if filter exists
48
        if ($this->crud()->hasFilterWhere('name', $options['name'])) {
49
            $properties = get_object_vars($this->crud()->firstFilterWhere('name', $options['name']));
0 ignored issues
show
Bug introduced by
$this->crud()->firstFilt...ame', $options['name']) of type boolean is incompatible with the type object expected by parameter $object of get_object_vars(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

49
            $properties = get_object_vars(/** @scrutinizer ignore-type */ $this->crud()->firstFilterWhere('name', $options['name']));
Loading history...
50
            foreach ($properties as $property => $value) {
51
                $this->{$property} = $value;
52
            }
53
        } else {
54
            // it means we're creating the filter now,
55
            $this->name = $options['name'];
56
            $this->key = Str::camel($options['name']);
57
            $this->type = $options['type'] ?? $this->type;
58
            $this->label = $options['label'] ?? $this->crud()->makeLabel($this->name);
59
            $this->viewNamespace = $options['viewNamespace'] ?? $options['view_namespace'] ?? $this->viewNamespace;
60
            $this->view = $this->type;
61
            $this->placeholder = $options['placeholder'] ?? '';
62
63
            $this->values = is_callable($values) ? $values() : $values;
64
            $this->options = $options;
65
            $this->logic = $logic;
66
            $this->fallbackLogic = $fallbackLogic;
67
        }
68
69
        if ($this->crud()->getRequest()->has($this->name)) {
70
            $this->currentValue = $this->crud()->getRequest()->input($this->name);
71
        }
72
    }
73
74
    /**
75
     * Check if the field is currently active. This happens when there's a GET parameter
76
     * in the current request with the same name as the name of the field.
77
     *
78
     * @return bool
79
     */
80
    public function isActive()
81
    {
82
        if ($this->crud()->getRequest()->has($this->name)) {
83
            return true;
84
        }
85
86
        return false;
87
    }
88
89
    /**
90
     * Check if the filter has already had the apply method called on it.
91
     *
92
     * @return bool
93
     */
94
    public function wasApplied()
95
    {
96
        return $this->applied;
97
    }
98
99
    /**
100
     * Check if the filter has not had the apply method called on it yet.
101
     * This is the inverse of the wasApplied() method.
102
     *
103
     * @return bool
104
     */
105
    public function wasNotApplied()
106
    {
107
        return ! $this->applied;
108
    }
109
110
    /**
111
     * Run the filter logic, default logic and/or fallback logic so that from this point on
112
     * the CRUD query has its results filtered, according to the Request.
113
     *
114
     * @param  array  $input  The GET parameters for which the filter should be applied.
115
     * @return void
116
     */
117
    public function apply($input = null)
118
    {
119
        // mark the field as already applied
120
        $this->applied(true);
121
122
        if (is_array($input)) {
123
            $input = new ParameterBag($input);
124
        }
125
126
        $input = $input ?? new ParameterBag($this->crud()->getRequest()->all());
127
128
        if (! $input->has($this->name)) {
129
            // if fallback logic was supplied and is a closure
130
            if (is_callable($this->fallbackLogic)) {
131
                return ($this->fallbackLogic)();
132
            }
133
134
            return;
135
        }
136
137
        // if a closure was passed as "filterLogic"
138
        if (is_callable($this->logic)) {
139
            return ($this->logic)($input->get($this->name));
140
        } else {
141
            return $this->applyDefaultLogic($this->name, false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type string expected by parameter $operator of Backpack\CRUD\app\Librar...er::applyDefaultLogic(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

141
            return $this->applyDefaultLogic($this->name, /** @scrutinizer ignore-type */ false);
Loading history...
Bug introduced by
Are you sure the usage of $this->applyDefaultLogic($this->name, false) targeting Backpack\CRUD\app\Librar...er::applyDefaultLogic() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
142
        }
143
    }
144
145
    /**
146
     * Get the full path of the filter view, including the view namespace.
147
     *
148
     * @return string
149
     */
150
    public function getViewWithNamespace()
151
    {
152
        return $this->viewNamespace.'.'.$this->view;
153
    }
154
155
    /**
156
     * Get an array of full paths to the filter view, including fallbacks
157
     * as configured in the backpack/config/crud.php file.
158
     *
159
     * @return array
160
     */
161
    public function getNamespacedViewWithFallbacks()
162
    {
163
        $type = $this->type;
164
        $namespaces = ViewNamespaces::getFor('filters');
165
166
        if ($this->viewNamespace != 'crud::filters') {
167
            $namespaces = array_merge([$this->viewNamespace], $namespaces);
168
        }
169
170
        return array_map(function ($item) use ($type) {
171
            return $item.'.'.$type;
172
        }, $namespaces);
173
    }
174
175
    // ---------------------
176
    // FLUENT SYNTAX METHODS
177
    // ---------------------
178
179
    /**
180
     * Create a CrudFilter object with the parameter as its name.
181
     *
182
     * @param  string  $name  Name of the column in the db, or model attribute.
183
     * @return CrudPanel
184
     */
185
    public static function name($name)
186
    {
187
        $filter = new static(compact('name'), null, null, null);
188
189
        return $filter->save();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $filter->save() returns the type Backpack\CRUD\app\Library\CrudPanel\CrudFilter which is incompatible with the documented return type Backpack\CRUD\app\Library\CrudPanel\CrudPanel.
Loading history...
190
    }
191
192
    /**
193
     * Remove the current filter from the current operation.
194
     *
195
     * @return void
196
     */
197
    public function remove()
198
    {
199
        $this->crud()->removeFilter($this->name);
200
    }
201
202
    /**
203
     * Remove an attribute from the current filter definition array.
204
     *
205
     * @param  string  $attribute  Name of the attribute being removed.
206
     * @return CrudFilter
207
     */
208
    public function forget($attribute)
209
    {
210
        if (property_exists($this, $attribute)) {
211
            $this->{$attribute} = false;
212
        }
213
214
        if (isset($this->options[$attribute])) {
215
            unset($this->options[$attribute]);
216
        }
217
218
        $this->crud()->replaceFilter($this->name, $this);
219
220
        return $this;
221
    }
222
223
    /**
224
     * Remove an attribute from one field's definition array.
225
     * (ununsed function).
226
     *
227
     * @param  string  $field  The name of the field.
228
     * @param  string  $attribute  The name of the attribute being removed.
229
     *
230
     * @codeCoverageIgnore
231
     *
232
     * @deprecated
233
     */
234
    public function removeFilterAttribute($filter, $attribute)
0 ignored issues
show
Unused Code introduced by
The parameter $filter is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

234
    public function removeFilterAttribute(/** @scrutinizer ignore-unused */ $filter, $attribute)

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

Loading history...
235
    {
236
        $fields = $this->fields();
0 ignored issues
show
Bug introduced by
The method fields() does not exist on Backpack\CRUD\app\Library\CrudPanel\CrudFilter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

236
        /** @scrutinizer ignore-call */ 
237
        $fields = $this->fields();
Loading history...
237
238
        unset($fields[$field][$attribute]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $field does not exist. Did you maybe mean $fields?
Loading history...
239
240
        $this->setOperationSetting('fields', $fields);
0 ignored issues
show
Bug introduced by
The method setOperationSetting() does not exist on Backpack\CRUD\app\Library\CrudPanel\CrudFilter. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

240
        $this->/** @scrutinizer ignore-call */ 
241
               setOperationSetting('fields', $fields);
Loading history...
241
    }
242
243
    /**
244
     * Move the current filter after another filter.
245
     *
246
     * @param  string  $destination  Name of the destination filter.
247
     * @return CrudFilter
248
     */
249
    public function after($destination)
250
    {
251
        $this->crud()->moveFilter($this->name, 'after', $destination);
252
253
        return $this;
254
    }
255
256
    /**
257
     * Move the current field before another field.
258
     *
259
     * @param  string  $destination  Name of the destination field.
260
     * @return CrudFilter
261
     */
262
    public function before($destination)
263
    {
264
        $this->crud()->moveFilter($this->name, 'before', $destination);
265
266
        return $this;
267
    }
268
269
    /**
270
     * Make the current field the first one in the fields list.
271
     *
272
     * @return CrudPanel
273
     */
274
    public function makeFirst()
275
    {
276
        $this->crud()->moveFilter($this->name, 'before', $this->crud()->filters()->first()->name);
277
278
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Backpack\CRUD\app\Library\CrudPanel\CrudFilter which is incompatible with the documented return type Backpack\CRUD\app\Library\CrudPanel\CrudPanel.
Loading history...
279
    }
280
281
    /**
282
     * Make the current field the last one in the fields list.
283
     *
284
     * @return CrudPanel
285
     */
286
    public function makeLast()
287
    {
288
        $this->crud()->removeFilter($this->name);
289
        $this->crud()->addCrudFilter($this);
290
291
        return $this;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this returns the type Backpack\CRUD\app\Library\CrudPanel\CrudFilter which is incompatible with the documented return type Backpack\CRUD\app\Library\CrudPanel\CrudPanel.
Loading history...
292
    }
293
294
    // -----------------------
295
    // FILTER-SPECIFIC SETTERS
296
    // -----------------------
297
298
    /**
299
     * Set the type of the filter.
300
     *
301
     * @param  string  $value  Name of blade view that shows the field.
302
     * @return CrudFilter
303
     */
304
    public function type($value)
305
    {
306
        $this->type = $value;
307
        $this->view = $value;
308
309
        return $this->save();
310
    }
311
312
    /**
313
     * Set the label of the filter - the element that the end-user can see and click
314
     * to activate the filter or an input that will activate the filter.
315
     *
316
     * @param  string  $value  A name for this filter that the end-user will understand.
317
     * @return CrudFilter
318
     */
319
    public function label($value)
320
    {
321
        $this->label = $value;
322
323
        return $this->save();
324
    }
325
326
    /**
327
     * Set the values for the current filter, for the filters who need values.
328
     * For example, the dropdown, select2 and select2 filters let the user select
329
     * pre-determined values to filter with. This is how to set those values that will be picked up.
330
     *
331
     * @param  array|function  $value  Key-value array with values for the user to pick from, or a function which also return a Key-value array.
0 ignored issues
show
Bug introduced by
The type Backpack\CRUD\app\Library\CrudPanel\function was not found. Maybe you did not declare it correctly or list all dependencies?

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:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
332
     * @return CrudFilter
333
     */
334
    public function values($value)
335
    {
336
        $this->values = (! is_string($value) && is_callable($value)) ? $value() : $value;
0 ignored issues
show
introduced by
The condition is_string($value) is always false.
Loading history...
337
338
        return $this->save();
339
    }
340
341
    /**
342
     * Set the values for the current filter, for the filters who need values. For example
343
     * the dropdown, select2 and select2 filters let the user select pre-determined
344
     * values to filter with.
345
     *
346
     * Alias of the values() method.
347
     *
348
     * @param  array|function  $value  Key-value array with values for the user to pick from, or a function which also return a Key-value array.
349
     * @return CrudFilter
350
     */
351
    public function options($value)
352
    {
353
        return $this->values($value);
354
    }
355
356
    /**
357
     * Set the blade view that will be used by the filter.
358
     * Should NOT include the namespace, that's defined separately using 'viewNamespace'.
359
     *
360
     * @param  string  $value  Path to the blade file, after the view namespace.
361
     * @return CrudFilter
362
     */
363
    public function view($value)
364
    {
365
        $this->view = $value;
366
367
        return $this->save();
368
    }
369
370
    /**
371
     * The path to the blade views directory where the filter file will be found. Ex: 'crud::filters'
372
     * Useful to load filters from a different package or directory.
373
     *
374
     * @param  string  $value  Blade path to the directory.
375
     * @return CrudFilter
376
     */
377
    public function viewNamespace($value)
378
    {
379
        $this->viewNamespace = $value;
380
381
        return $this->save();
382
    }
383
384
    /**
385
     * Define what happens when the filter is active, through a closure.
386
     *
387
     * @param  Closure  $value  Closure that will be called when Request has this name as GET parameter.
388
     * @return CrudFilter
389
     */
390
    public function logic($value)
391
    {
392
        $this->logic = $value;
393
394
        return $this->save();
395
    }
396
397
    /**
398
     * Define what happens when the filter is NOT active, through a closure.
399
     *
400
     * @param  Closure  $value  Closure that will be called when Request does NOT have this name as GET parameter.
401
     * @return CrudFilter
402
     */
403
    public function fallbackLogic($value)
404
    {
405
        $this->fallbackLogic = $value;
406
407
        return $this->save();
408
    }
409
410
    /**
411
     * Define if the filter has already been applied (logic or fallbackLogic called).
412
     *
413
     * @param  bool  $value  Whether the filter has been run.
414
     * @return CrudFilter
415
     */
416
    public function applied($value)
417
    {
418
        $this->applied = $value;
419
420
        return $this->save();
421
    }
422
423
    /**
424
     * Aliases of the logic() method.
425
     */
426
    public function whenActive($value)
427
    {
428
        return $this->logic($value);
429
    }
430
431
    public function ifActive($value)
432
    {
433
        return $this->logic($value);
434
    }
435
436
    /**
437
     * Alises of the fallbackLogic() method.
438
     */
439
    public function whenInactive($value)
440
    {
441
        return $this->fallbackLogic($value);
442
    }
443
444
    public function whenNotActive($value)
445
    {
446
        return $this->fallbackLogic($value);
447
    }
448
449
    public function ifInactive($value)
450
    {
451
        return $this->fallbackLogic($value);
452
    }
453
454
    public function ifNotActive($value)
455
    {
456
        return $this->fallbackLogic($value);
457
    }
458
459
    public function else($value)
460
    {
461
        return $this->fallbackLogic($value);
462
    }
463
464
    // ---------------
465
    // PRIVATE METHODS
466
    // ---------------
467
468
    private function crud()
469
    {
470
        return app()->make('crud');
471
    }
472
473
    /**
474
     * Set the value for a certain attribute on the CrudFilter object.
475
     *
476
     * @param  string  $attribute  Name of the attribute.
477
     * @param  string  $value  Value of that attribute.
478
     */
479
    private function setOptionValue($attribute, $value)
480
    {
481
        $this->options[$attribute] = $value;
482
    }
483
484
    /**
485
     * Replace all field options on the CrudFilter object
486
     * with the given array of attribute-value pairs.
487
     *
488
     * @param  array  $array  Array of options and their values.
489
     */
490
    private function setAllOptionsValues($array)
0 ignored issues
show
Unused Code introduced by
The method setAllOptionsValues() is not used, and could be removed.

This check looks for private methods that have been defined, but are not used inside the class.

Loading history...
491
    {
492
        $this->options = $array;
493
    }
494
495
    /**
496
     * Update the global CrudPanel object with the current field options.
497
     *
498
     * @return CrudFilter
499
     */
500
    private function save()
501
    {
502
        $key = $this->name;
503
504
        if ($this->crud()->hasFilterWhere('name', $key)) {
505
            $this->crud()->modifyFilter($key, (array) $this);
506
        } else {
507
            $this->crud()->addCrudFilter($this);
508
        }
509
510
        return $this;
511
    }
512
513
    /**
514
     * @param  string  $name
515
     * @param  string  $operator
516
     * @param  array  $input
517
     */
518
    private function applyDefaultLogic($name, $operator, $input = null)
519
    {
520
        $input = $input ?? $this->crud()->getRequest()->all();
521
522
        // if this filter is active (the URL has it as a GET parameter)
523
        switch ($operator) {
524
            // if no operator was passed, just use the equals operator
525
            case false:
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $operator of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
526
                $this->crud()->addClause('where', $name, $input[$name]);
527
                break;
528
529
            case 'scope':
530
                $this->crud()->addClause($operator);
531
                break;
532
533
                // TODO:
534
                // whereBetween
535
                // whereNotBetween
536
                // whereIn
537
                // whereNotIn
538
                // whereNull
539
                // whereNotNull
540
                // whereDate
541
                // whereMonth
542
                // whereDay
543
                // whereYear
544
                // whereColumn
545
                // like
546
547
                // sql comparison operators
548
            case '=':
549
            case '<=>':
550
            case '<>':
551
            case '!=':
552
            case '>':
553
            case '>=':
554
            case '<':
555
            case '<=':
556
                $this->crud()->addClause('where', $name, $operator, $input[$name]);
557
                break;
558
559
            default:
560
                abort(500, 'Unknown filter operator.');
561
                break;
562
        }
563
    }
564
565
    // -----------------
566
    // DEBUGGING METHODS
567
    // -----------------
568
569
    /**
570
     * Dump the current object to the screen,
571
     * so that the developer can see its contents.
572
     *
573
     * @codeCoverageIgnore
574
     *
575
     * @return CrudFilter
576
     */
577
    public function dump()
578
    {
579
        dump($this);
580
581
        return $this;
582
    }
583
584
    /**
585
     * Dump and die. Duumps the current object to the screen,
586
     * so that the developer can see its contents, then stops
587
     * the execution.
588
     *
589
     * @codeCoverageIgnore
590
     *
591
     * @return CrudFilter
592
     */
593
    public function dd()
594
    {
595
        dd($this);
596
597
        return $this;
598
    }
599
600
    // -------------
601
    // MAGIC METHODS
602
    // -------------
603
604
    /**
605
     * If a developer calls a method that doesn't exist, assume they want:
606
     * - $this->options['whatever'] to be set to that value;
607
     * - that filter be updated inside the global CrudPanel object;.
608
     *
609
     * Eg: type('number') will set the "type" attribute to "number"
610
     *
611
     * @param  string  $method  The method being called that doesn't exist.
612
     * @param  array  $parameters  The arguments when that method was called.
613
     * @return CrudFilter
614
     */
615
    public function __call($method, $parameters)
616
    {
617
        $this->setOptionValue($method, $parameters[0]);
618
619
        return $this->save();
620
    }
621
}
622