Completed
Push — master ( 777a36...4cd4e4 )
by Song
02:30
created

Filter::removeDefaultIDFilter()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Grid;
4
5
use Encore\Admin\Grid\Filter\AbstractFilter;
6
use Encore\Admin\Grid\Filter\Group;
7
use Encore\Admin\Grid\Filter\Layout\Layout;
8
use Encore\Admin\Grid\Filter\Scope;
9
use Illuminate\Contracts\Support\Arrayable;
10
use Illuminate\Contracts\Support\Renderable;
11
use Illuminate\Support\Collection;
12
use Illuminate\Support\Facades\Input;
13
14
/**
15
 * Class Filter.
16
 *
17
 * @method AbstractFilter     equal($column, $label = '')
18
 * @method AbstractFilter     notEqual($column, $label = '')
19
 * @method AbstractFilter     like($column, $label = '')
20
 * @method AbstractFilter     ilike($column, $label = '')
21
 * @method AbstractFilter     gt($column, $label = '')
22
 * @method AbstractFilter     lt($column, $label = '')
23
 * @method AbstractFilter     between($column, $label = '')
24
 * @method AbstractFilter     in($column, $label = '')
25
 * @method AbstractFilter     notIn($column, $label = '')
26
 * @method AbstractFilter     where($callback, $label)
27
 * @method AbstractFilter     date($column, $label = '')
28
 * @method AbstractFilter     day($column, $label = '')
29
 * @method AbstractFilter     month($column, $label = '')
30
 * @method AbstractFilter     year($column, $label = '')
31
 * @method AbstractFilter     hidden($name, $value)
32
 * @method AbstractFilter     group($column, $label = '', $builder = null)
33
 */
34
class Filter implements Renderable
35
{
36
    /**
37
     * @var Model
38
     */
39
    protected $model;
40
41
    /**
42
     * @var array
43
     */
44
    protected $filters = [];
45
46
    /**
47
     * @var array
48
     */
49
    protected $supports = [
50
        'equal', 'notEqual', 'ilike', 'like', 'gt', 'lt', 'between', 'group',
51
        'where', 'in', 'notIn', 'date', 'day', 'month', 'year', 'hidden',
52
    ];
53
54
    /**
55
     * If use id filter.
56
     *
57
     * @var bool
58
     */
59
    protected $useIdFilter = true;
60
61
    /**
62
     * Id filter was removed.
63
     *
64
     * @var bool
65
     */
66
    protected $idFilterRemoved = false;
67
68
    /**
69
     * Action of search form.
70
     *
71
     * @var string
72
     */
73
    protected $action;
74
75
    /**
76
     * @var string
77
     */
78
    protected $view = 'admin::filter.container';
79
80
    /**
81
     * @var string
82
     */
83
    protected $filterID = 'filter-box';
84
85
    /**
86
     * @var string
87
     */
88
    protected $name = '';
89
90
    /**
91
     * @var bool
92
     */
93
    public $expand = false;
94
95
    /**
96
     * @var Collection
97
     */
98
    protected $scopes;
99
100
    /**
101
     * @var Layout
102
     */
103
    protected $layout;
104
105
    /**
106
     * Primary key of giving model.
107
     *
108
     * @var mixed
109
     */
110
    protected $primaryKey;
111
112
    /**
113
     * Create a new filter instance.
114
     *
115
     * @param Model $model
116
     */
117
    public function __construct(Model $model)
118
    {
119
        $this->model = $model;
120
121
        $this->primaryKey = $this->model->eloquent()->getKeyName();
122
123
        $this->initLayout();
124
125
        $this->equal($this->primaryKey, strtoupper($this->primaryKey));
126
        $this->scopes = new Collection();
127
    }
128
129
    /**
130
     * Initialize filter layout.
131
     */
132
    protected function initLayout()
133
    {
134
        $this->layout = new Filter\Layout\Layout($this);
135
    }
136
137
    /**
138
     * Set action of search form.
139
     *
140
     * @param string $action
141
     *
142
     * @return $this
143
     */
144
    public function setAction($action)
145
    {
146
        $this->action = $action;
147
148
        return $this;
149
    }
150
151
    /**
152
     * Get grid model.
153
     *
154
     * @return Model
155
     */
156
    public function getModel()
157
    {
158
        return $this->model;
159
    }
160
161
    /**
162
     * Set ID of search form.
163
     *
164
     * @param string $filterID
165
     *
166
     * @return $this
167
     */
168
    public function setFilterID($filterID)
169
    {
170
        $this->filterID = $filterID;
171
172
        return $this;
173
    }
174
175
    /**
176
     * Get filter ID.
177
     *
178
     * @return string
179
     */
180
    public function getFilterID()
181
    {
182
        return $this->filterID;
183
    }
184
185
    /**
186
     * @param $name
187
     *
188
     * @return $this
189
     */
190
    public function setName($name)
191
    {
192
        $this->name = $name;
193
194
        $this->setFilterID("{$this->name}-{$this->filterID}");
195
196
        return $this;
197
    }
198
199
    /**
200
     * @return string
201
     */
202
    public function getName()
203
    {
204
        return $this->name;
205
    }
206
207
    /**
208
     * Disable Id filter.
209
     *
210
     * @return $this
211
     */
212
    public function disableIdFilter()
213
    {
214
        $this->useIdFilter = false;
215
216
        return $this;
217
    }
218
219
    /**
220
     * Remove ID filter if needed.
221
     */
222
    public function removeIDFilterIfNeeded()
223
    {
224
        if (!$this->useIdFilter && !$this->idFilterRemoved) {
225
            $this->removeDefaultIDFilter();
226
227
            $this->layout->removeDefaultIDFilter();
228
229
            $this->idFilterRemoved = true;
230
        }
231
    }
232
233
    /**
234
     * Remove the default ID filter.
235
     */
236
    protected function removeDefaultIDFilter()
237
    {
238
        array_shift($this->filters);
239
    }
240
241
    /**
242
     * Remove filter by filter id.
243
     *
244
     * @param mixed $id
245
     */
246
    protected function removeFilterByID($id)
247
    {
248
        $this->filters = array_filter($this->filters, function (AbstractFilter $filter) use ($id) {
249
            return $filter->getId() != $id;
250
        });
251
    }
252
253
    /**
254
     * Get all conditions of the filters.
255
     *
256
     * @return array
257
     */
258
    public function conditions()
259
    {
260
        $inputs = array_dot(Input::all());
261
262
        $inputs = array_filter($inputs, function ($input) {
263
            return $input !== '' && !is_null($input);
264
        });
265
266
        $this->sanitizeInputs($inputs);
267
268
        if (empty($inputs)) {
269
            return [];
270
        }
271
272
        $params = [];
273
274
        foreach ($inputs as $key => $value) {
275
            array_set($params, $key, $value);
276
        }
277
278
        $conditions = [];
279
280
        $this->removeIDFilterIfNeeded();
281
282
        foreach ($this->filters() as $filter) {
283
            $conditions[] = $filter->condition($params);
284
        }
285
286
        return tap(array_filter($conditions), function ($conditions) {
287
            if (!empty($conditions)) {
288
                $this->expand();
289
            }
290
        });
291
    }
292
293
    /**
294
     * @param $inputs
295
     *
296
     * @return array
297
     */
298
    protected function sanitizeInputs(&$inputs)
299
    {
300
        if (!$this->name) {
301
            return $inputs;
302
        }
303
304
        $inputs = collect($inputs)->filter(function ($input, $key) {
305
            return starts_with($key, "{$this->name}_");
306
        })->mapWithKeys(function ($val, $key) {
307
            $key = str_replace("{$this->name}_", '', $key);
308
309
            return [$key => $val];
310
        })->toArray();
311
    }
312
313
    /**
314
     * Add a filter to grid.
315
     *
316
     * @param AbstractFilter $filter
317
     *
318
     * @return AbstractFilter
319
     */
320
    protected function addFilter(AbstractFilter $filter)
321
    {
322
        $this->layout->addFilter($filter);
323
324
        $filter->setParent($this);
325
326
        return $this->filters[] = $filter;
327
    }
328
329
    /**
330
     * Use a custom filter.
331
     *
332
     * @param AbstractFilter $filter
333
     *
334
     * @return AbstractFilter
335
     */
336
    public function use(AbstractFilter $filter)
337
    {
338
        return $this->addFilter($filter);
339
    }
340
341
    /**
342
     * Get all filters.
343
     *
344
     * @return AbstractFilter[]
345
     */
346
    public function filters()
347
    {
348
        return $this->filters;
349
    }
350
351
    /**
352
     * @param string $key
353
     * @param string $label
354
     *
355
     * @return mixed
356
     */
357
    public function scope($key, $label = '')
358
    {
359
        return tap(new Scope($key, $label), function (Scope $scope) {
360
            return $this->scopes->push($scope);
361
        });
362
    }
363
364
    /**
365
     * Get all filter scopes.
366
     *
367
     * @return Collection
368
     */
369
    public function getScopes()
370
    {
371
        return $this->scopes;
372
    }
373
374
    /**
375
     * Get current scope.
376
     *
377
     * @return Scope|null
378
     */
379
    public function getCurrentScope()
380
    {
381
        $key = request(Scope::QUERY_NAME);
382
383
        return $this->scopes->first(function ($scope) use ($key) {
384
            return $scope->key == $key;
385
        });
386
    }
387
388
    /**
389
     * Get scope conditions.
390
     *
391
     * @return array
392
     */
393
    protected function scopeConditions()
394
    {
395
        if ($scope = $this->getCurrentScope()) {
396
            return $scope->condition();
397
        }
398
399
        return [];
400
    }
401
402
    /**
403
     * Add a new layout column.
404
     *
405
     * @param int      $width
406
     * @param \Closure $closure
407
     *
408
     * @return $this
409
     */
410
    public function column($width, \Closure $closure)
411
    {
412
        $width = $width < 1 ? round(12 * $width) : $width;
413
414
        $this->layout->column($width, $closure);
415
416
        return $this;
417
    }
418
419
    /**
420
     * Expand filter container.
421
     *
422
     * @return $this
423
     */
424
    public function expand()
425
    {
426
        $this->expand = true;
427
428
        return $this;
429
    }
430
431
    /**
432
     * Execute the filter with conditions.
433
     *
434
     * @param bool $toArray
435
     *
436
     * @return array|Collection|mixed
437
     */
438 View Code Duplication
    public function execute($toArray = true)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
439
    {
440
        $conditions = array_merge(
441
            $this->conditions(),
442
            $this->scopeConditions()
443
        );
444
445
        return $this->model->addConditions($conditions)->buildData($toArray);
446
    }
447
448
    /**
449
     * @param callable $callback
450
     * @param int      $count
451
     *
452
     * @return bool
453
     */
454 View Code Duplication
    public function chunk(callable $callback, $count = 100)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
455
    {
456
        $conditions = array_merge(
457
            $this->conditions(),
458
            $this->scopeConditions()
459
        );
460
461
        return $this->model->addConditions($conditions)->chunk($callback, $count);
462
    }
463
464
    /**
465
     * Get the string contents of the filter view.
466
     *
467
     * @return \Illuminate\View\View|string
468
     */
469
    public function render()
470
    {
471
        $this->removeIDFilterIfNeeded();
472
473
        if (empty($this->filters)) {
474
            return '';
475
        }
476
477
        return view($this->view)->with([
0 ignored issues
show
Bug introduced by
The method with does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
478
            'action'   => $this->action ?: $this->urlWithoutFilters(),
479
            'layout'   => $this->layout,
480
            'filterID' => $this->filterID,
481
            'expand'   => $this->expand,
482
        ])->render();
483
    }
484
485
    /**
486
     * Get url without filter queryString.
487
     *
488
     * @return string
489
     */
490
    public function urlWithoutFilters()
491
    {
492
        /** @var Collection $columns */
493
        $columns = collect($this->filters)->map->getColumn();
494
495
        $pageKey = 'page';
496
497
        if ($gridName = $this->model->getGrid()->getName()) {
498
            $pageKey = "{$gridName}_{$pageKey}";
499
        }
500
501
        $columns->push($pageKey);
502
503
        $groupNames = collect($this->filters)->filter(function ($filter) {
504
            return $filter instanceof Group;
505
        })->map(function (AbstractFilter $filter) {
506
            return "{$filter->getId()}_group";
507
        });
508
509
        return $this->fullUrlWithoutQuery(
510
            $columns->merge($groupNames)
511
        );
512
    }
513
514
    /**
515
     * Get url without scope queryString.
516
     *
517
     * @return string
518
     */
519
    public function urlWithoutScopes()
520
    {
521
        return $this->fullUrlWithoutQuery(Scope::QUERY_NAME);
522
    }
523
524
    /**
525
     * Get full url without query strings.
526
     *
527
     * @param Arrayable|array|string $keys
528
     *
529
     * @return string
530
     */
531
    protected function fullUrlWithoutQuery($keys)
532
    {
533
        if ($keys instanceof Arrayable) {
534
            $keys = $keys->toArray();
535
        }
536
537
        $keys = (array) $keys;
538
539
        $request = request();
540
541
        $query = $request->query();
542
        array_forget($query, $keys);
543
544
        $question = $request->getBaseUrl().$request->getPathInfo() == '/' ? '/?' : '?';
545
546
        return count($request->query()) > 0
547
            ? $request->url().$question.http_build_query($query)
548
            : $request->fullUrl();
549
    }
550
551
    /**
552
     * Generate a filter object and add to grid.
553
     *
554
     * @param string $method
555
     * @param array  $arguments
556
     *
557
     * @return AbstractFilter|$this
558
     */
559
    public function __call($method, $arguments)
560
    {
561
        if (in_array($method, $this->supports)) {
562
            $className = '\\Encore\\Admin\\Grid\\Filter\\'.ucfirst($method);
563
564
            return $this->addFilter(new $className(...$arguments));
565
        }
566
567
        return $this;
568
    }
569
}
570