Completed
Pull Request — master (#3154)
by
unknown
02:22
created

Grid   F

Complexity

Total Complexity 122

Size/Duplication

Total Lines 1208
Duplicated Lines 3.81 %

Coupling/Cohesion

Components 1
Dependencies 17

Importance

Changes 0
Metric Value
dl 46
loc 1208
rs 0.8
c 0
b 0
f 0
wmc 122
lcom 1
cbo 17

68 Methods

Rating   Name   Duplication   Size   Complexity  
A showTools() 0 4 1
A showExportBtn() 0 4 1
A disableExport() 0 4 1
A renderExportButton() 0 4 1
A disableCreation() 0 4 1
A disableCreateButton() 0 4 1
A showCreateBtn() 0 4 1
A renderCreateButton() 0 4 1
A __construct() 0 17 1
A init() 0 4 1
A callInitCallbacks() 0 10 3
A setupTools() 0 4 1
A setupFilter() 0 4 1
A handleExportRequest() 0 23 5
A getExporter() 0 4 1
A option() 0 10 2
A getKeyName() 0 4 2
A column() 0 23 5
A columns() 0 18 6
A visibleColumns() 14 14 2
A visibleColumnNames() 14 14 2
A addColumn() 9 9 1
A prependColumn() 9 9 1
A model() 0 4 1
A paginate() 0 6 1
A paginator() 0 4 1
A disablePagination() 0 6 1
A showPagination() 0 4 1
A perPages() 0 4 1
A disableActions() 0 4 1
A actions() 0 12 4
A appendActionsColumn() 0 9 2
A disableRowSelector() 0 6 1
A prependRowSelectorColumn() 0 9 2
A build() 0 25 2
A disableTools() 0 4 1
A disableFilter() 0 6 1
A getFilter() 0 4 1
A processFilter() 0 8 2
A filter() 0 4 1
A renderFilter() 0 8 2
A expandFilter() 0 6 1
A buildRows() 0 10 2
A rows() 0 8 2
A tools() 0 4 1
A renderHeaderTools() 0 4 1
A exporter() 0 6 1
A getExportUrl() 0 10 2
A getCreateUrl() 0 13 3
A disableColumnSelector() 0 4 1
A showColumnSelector() 0 4 1
A renderColumnSelector() 0 4 1
A header() 0 10 2
A renderHeader() 0 8 2
A footer() 0 10 2
A renderFooter() 0 8 2
A resource() 0 14 3
A handleGetMutatorColumn() 0 8 2
B handleRelationColumn() 0 32 9
A __call() 0 18 5
A registerColumnDisplayer() 0 25 2
A with() 0 6 1
A variables() 0 6 1
A setView() 0 8 2
A setTitle() 0 6 1
A setRelation() 0 6 1
A setResource() 0 6 1
A render() 0 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

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

1
<?php
2
3
namespace Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Exception\Handler;
7
use Encore\Admin\Grid\Column;
8
use Encore\Admin\Grid\Displayers;
9
use Encore\Admin\Grid\Exporter;
10
use Encore\Admin\Grid\Exporters\AbstractExporter;
11
use Encore\Admin\Grid\Filter;
12
use Encore\Admin\Grid\HasElementNames;
13
use Encore\Admin\Grid\Model;
14
use Encore\Admin\Grid\Row;
15
use Encore\Admin\Grid\Tools;
16
use Illuminate\Database\Eloquent\Model as Eloquent;
17
use Illuminate\Database\Eloquent\Relations;
18
use Illuminate\Support\Collection;
19
use Illuminate\Support\Facades\Input;
20
use Jenssegers\Mongodb\Eloquent\Model as MongodbModel;
21
22
class Grid
23
{
24
    use HasElementNames;
25
26
    /**
27
     * The grid data model instance.
28
     *
29
     * @var \Encore\Admin\Grid\Model
30
     */
31
    protected $model;
32
33
    /**
34
     * Collection of all grid columns.
35
     *
36
     * @var \Illuminate\Support\Collection
37
     */
38
    protected $columns;
39
40
    /**
41
     * Collection of all data rows.
42
     *
43
     * @var \Illuminate\Support\Collection
44
     */
45
    protected $rows;
46
47
    /**
48
     * Rows callable fucntion.
49
     *
50
     * @var \Closure
51
     */
52
    protected $rowsCallback;
53
54
    /**
55
     * All column names of the grid.
56
     *
57
     * @var array
58
     */
59
    public $columnNames = [];
60
61
    /**
62
     * Grid builder.
63
     *
64
     * @var \Closure
65
     */
66
    protected $builder;
67
68
    /**
69
     * Mark if the grid is builded.
70
     *
71
     * @var bool
72
     */
73
    protected $builded = false;
74
75
    /**
76
     * All variables in grid view.
77
     *
78
     * @var array
79
     */
80
    protected $variables = [];
81
82
    /**
83
     * The grid Filter.
84
     *
85
     * @var \Encore\Admin\Grid\Filter
86
     */
87
    protected $filter;
88
89
    /**
90
     * Resource path of the grid.
91
     *
92
     * @var
93
     */
94
    protected $resourcePath;
95
96
    /**
97
     * Default primary key name.
98
     *
99
     * @var string
100
     */
101
    protected $keyName = 'id';
102
103
    /**
104
     * Export driver.
105
     *
106
     * @var string
107
     */
108
    protected $exporter;
109
110
    /**
111
     * View for grid to render.
112
     *
113
     * @var string
114
     */
115
    protected $view = 'admin::grid.table';
116
117
    /**
118
     * Per-page options.
119
     *
120
     * @var array
121
     */
122
    public $perPages = [10, 20, 30, 50, 100];
123
124
    /**
125
     * Default items count per-page.
126
     *
127
     * @var int
128
     */
129
    public $perPage = 20;
130
131
    /**
132
     * Header tools.
133
     *
134
     * @var Tools
135
     */
136
    public $tools;
137
138
    /**
139
     * Callback for grid actions.
140
     *
141
     * @var Closure
142
     */
143
    protected $actionsCallback;
144
145
    /**
146
     * Actions column display class.
147
     *
148
     * @var string
149
     */
150
    protected $actionsClass = Displayers\Actions::class;
151
152
    /**
153
     * Options for grid.
154
     *
155
     * @var array
156
     */
157
    protected $options = [
158
        'show_pagination'   => true,
159
        'show_tools'        => true,
160
        'show_filter'       => true,
161
        'show_exporter'     => true,
162
        'show_actions'      => true,
163
        'show_row_selector' => true,
164
        'show_create_btn'   => true,
165
        'show_column_selector'   => true,
166
    ];
167
168
    /**
169
     * @var Closure
170
     */
171
    protected $header;
172
173
    /**
174
     * @var Closure
175
     */
176
    protected $footer;
177
178
    /**
179
     * Initialization closure array.
180
     *
181
     * @var []Closure
182
     */
183
    protected static $initCallbacks = [];
184
185
    /**
186
     * Create a new grid instance.
187
     *
188
     * @param Eloquent $model
189
     * @param Closure  $builder
190
     */
191
    public function __construct(Eloquent $model, Closure $builder = null)
192
    {
193
        $this->keyName = $model->getKeyName();
194
        $this->model = new Model($model);
195
        $this->columns = new Collection();
196
        $this->rows = new Collection();
197
        $this->builder = $builder;
198
199
        $this->model()->setGrid($this);
200
201
        $this->setupTools();
202
        $this->setupFilter();
203
204
        $this->handleExportRequest();
205
206
        $this->callInitCallbacks();
207
    }
208
209
    /**
210
     * Initialize with user pre-defined default disables and exporter, etc.
211
     *
212
     * @param Closure $callback
213
     */
214
    public static function init(Closure $callback = null)
215
    {
216
        static::$initCallbacks[] = $callback;
217
    }
218
219
    /**
220
     * Call the initialization closure array in sequence.
221
     */
222
    protected function callInitCallbacks()
223
    {
224
        if (empty(static::$initCallbacks)) {
225
            return;
226
        }
227
228
        foreach (static::$initCallbacks as $callback) {
229
            call_user_func($callback, $this);
230
        }
231
    }
232
233
    /**
234
     * Setup grid tools.
235
     */
236
    public function setupTools()
237
    {
238
        $this->tools = new Tools($this);
239
    }
240
241
    /**
242
     * Setup grid filter.
243
     *
244
     * @return void
245
     */
246
    protected function setupFilter()
247
    {
248
        $this->filter = new Filter($this->model());
249
    }
250
251
    /**
252
     * Handle export request.
253
     *
254
     * @param bool $forceExport
255
     */
256
    protected function handleExportRequest($forceExport = false)
257
    {
258
        if (!$scope = request(Exporter::$queryName)) {
259
            return;
260
        }
261
262
        // clear output buffer.
263
        if (ob_get_length()) {
264
            ob_end_clean();
265
        }
266
267
        $this->model()->usePaginate(false);
268
269
        if ($this->builder) {
270
            call_user_func($this->builder, $this);
271
272
            $this->getExporter($scope)->export();
273
        }
274
275
        if ($forceExport) {
276
            $this->getExporter($scope)->export();
277
        }
278
    }
279
280
    /**
281
     * @param string $scope
282
     *
283
     * @return AbstractExporter
284
     */
285
    protected function getExporter($scope)
286
    {
287
        return (new Exporter($this))->resolve($this->exporter)->withScope($scope);
288
    }
289
290
    /**
291
     * Get or set option for grid.
292
     *
293
     * @param string $key
294
     * @param mixed  $value
295
     *
296
     * @return $this|mixed
297
     */
298
    public function option($key, $value = null)
299
    {
300
        if (is_null($value)) {
301
            return $this->options[$key];
302
        }
303
304
        $this->options[$key] = $value;
305
306
        return $this;
307
    }
308
309
    /**
310
     * Get primary key name of model.
311
     *
312
     * @return string
313
     */
314
    public function getKeyName()
315
    {
316
        return $this->keyName ?: 'id';
317
    }
318
319
    /**
320
     * Add column to Grid.
321
     *
322
     * @param string $name
323
     * @param string $label
324
     *
325
     * @return Column
326
     */
327
    public function column($name, $label = '')
328
    {
329
        $relationName = $relationColumn = '';
330
331
        if (strpos($name, '.') !== false) {
332
            list($relationName, $relationColumn) = explode('.', $name);
333
334
            $relation = $this->model()->eloquent()->$relationName();
335
336
            $label = empty($label) ? ucfirst($relationColumn) : $label;
337
338
            $name = snake_case($relationName).'.'.$relationColumn;
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated with message: Str::snake() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
339
        }
340
341
        $column = $this->addColumn($name, $label);
342
343
        if (isset($relation) && $relation instanceof Relations\Relation) {
344
            $this->model()->with($relationName);
345
            $column->setRelation($relationName, $relationColumn);
346
        }
347
348
        return $column;
349
    }
350
351
    /**
352
     * Batch add column to grid.
353
     *
354
     * @example
355
     * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]);
356
     * 2.$grid->columns('name', 'email' ...)
357
     *
358
     * @param array $columns
359
     *
360
     * @return Collection|null
361
     */
362
    public function columns($columns = [])
363
    {
364
        if (func_num_args() == 0) {
365
            return $this->columns;
366
        }
367
368
        if (func_num_args() == 1 && is_array($columns)) {
369
            foreach ($columns as $column => $label) {
370
                $this->column($column, $label);
371
            }
372
373
            return;
374
        }
375
376
        foreach (func_get_args() as $column) {
377
            $this->column($column);
378
        }
379
    }
380
381
    /**
382
     * Get all visible column instances.
383
     *
384
     * @return Collection|static
385
     */
386 View Code Duplication
    public function visibleColumns()
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...
387
    {
388
        $visible = array_filter(explode(',', request(Tools\ColumnSelector::SELECT_COLUMN_NAME)));
389
390
        if (empty($visible)) {
391
            return $this->columns;
392
        }
393
394
        array_push($visible, '__row_selector__', '__actions__');
395
396
        return $this->columns->filter(function (Column $column) use ($visible) {
397
            return in_array($column->getName(), $visible);
398
        });
399
    }
400
401
    /**
402
     * Get all visible column names.
403
     *
404
     * @return array|static
405
     */
406 View Code Duplication
    public function visibleColumnNames()
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...
407
    {
408
        $visible = array_filter(explode(',', request(Tools\ColumnSelector::SELECT_COLUMN_NAME)));
409
410
        if (empty($visible)) {
411
            return $this->columnNames;
412
        }
413
414
        array_push($visible, '__row_selector__', '__actions__');
415
416
        return collect($this->columnNames)->filter(function ($column) use ($visible) {
417
            return in_array($column, $visible);
418
        });
419
    }
420
421
    /**
422
     * Add column to grid.
423
     *
424
     * @param string $column
425
     * @param string $label
426
     *
427
     * @return Column
428
     */
429 View Code Duplication
    protected function addColumn($column = '', $label = '')
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...
430
    {
431
        $column = new Column($column, $label);
432
        $column->setGrid($this);
433
434
        return tap($column, function ($value) {
435
            $this->columns->push($value);
436
        });
437
    }
438
439
    /**
440
     * Prepend column to grid.
441
     *
442
     * @param string $column
443
     * @param string $label
444
     *
445
     * @return Column
446
     */
447 View Code Duplication
    protected function prependColumn($column = '', $label = '')
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...
448
    {
449
        $column = new Column($column, $label);
450
        $column->setGrid($this);
451
452
        return tap($column, function ($value) {
453
            $this->columns->prepend($value);
454
        });
455
    }
456
457
    /**
458
     * Get Grid model.
459
     *
460
     * @return Model
461
     */
462
    public function model()
463
    {
464
        return $this->model;
465
    }
466
467
    /**
468
     * Paginate the grid.
469
     *
470
     * @param int $perPage
471
     *
472
     * @return void
473
     */
474
    public function paginate($perPage = 20)
475
    {
476
        $this->perPage = $perPage;
477
478
        $this->model()->paginate($perPage);
0 ignored issues
show
Bug introduced by
The method paginate() does not exist on Encore\Admin\Grid\Model. Did you maybe mean usePaginate()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
479
    }
480
481
    /**
482
     * Get the grid paginator.
483
     *
484
     * @return mixed
485
     */
486
    public function paginator()
487
    {
488
        return new Tools\Paginator($this);
489
    }
490
491
    /**
492
     * Disable grid pagination.
493
     *
494
     * @return $this
495
     */
496
    public function disablePagination(bool $disable = true)
497
    {
498
        $this->model->usePaginate(!$disable);
499
500
        return $this->option('show_pagination', !$disable);
501
    }
502
503
    /**
504
     * If this grid use pagination.
505
     *
506
     * @return bool
507
     */
508
    public function showPagination()
509
    {
510
        return $this->option('show_pagination');
511
    }
512
513
    /**
514
     * Set per-page options.
515
     *
516
     * @param array $perPages
517
     */
518
    public function perPages(array $perPages)
519
    {
520
        $this->perPages = $perPages;
521
    }
522
523
    /**
524
     * Disable all actions.
525
     *
526
     * @return $this
527
     */
528
    public function disableActions(bool $disable = true)
529
    {
530
        return $this->option('show_actions', !$disable);
531
    }
532
533
    /**
534
     * Set grid action callback.
535
     *
536
     * @param Closure|string $actions
537
     *
538
     * @return $this
539
     */
540
    public function actions($actions)
541
    {
542
        if ($actions instanceof Closure) {
543
            $this->actionsCallback = $actions;
544
        }
545
546
        if (is_string($actions) && is_subclass_of($actions, Displayers\Actions::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Encore\Admin\Grid\Displayers\Actions::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
547
            $this->actionsClass = $actions;
548
        }
549
550
        return $this;
551
    }
552
553
    /**
554
     * Add `actions` column for grid.
555
     *
556
     * @return void
557
     */
558
    protected function appendActionsColumn()
559
    {
560
        if (!$this->option('show_actions')) {
561
            return;
562
        }
563
564
        $this->addColumn('__actions__', trans('admin.action'))
565
            ->displayUsing($this->actionsClass, [$this->actionsCallback]);
566
    }
567
568
    /**
569
     * Disable row selector.
570
     *
571
     * @return Grid|mixed
572
     */
573
    public function disableRowSelector(bool $disable = true)
574
    {
575
        $this->tools->disableBatchActions($disable);
576
577
        return $this->option('show_row_selector', !$disable);
578
    }
579
580
    /**
581
     * Prepend checkbox column for grid.
582
     *
583
     * @return void
584
     */
585
    protected function prependRowSelectorColumn()
586
    {
587
        if (!$this->option('show_row_selector')) {
588
            return;
589
        }
590
591
        $this->prependColumn(Column::SELECT_COLUMN_NAME, ' ')
592
            ->displayUsing(Displayers\RowSelector::class);
593
    }
594
595
    /**
596
     * Build the grid.
597
     *
598
     * @return void
599
     */
600
    public function build()
601
    {
602
        if ($this->builded) {
603
            return;
604
        }
605
606
        $collection = $this->processFilter(false);
607
608
        $data = $collection->toArray();
609
610
        $this->prependRowSelectorColumn();
611
        $this->appendActionsColumn();
612
613
        Column::setOriginalGridModels($collection);
614
615
        $this->columns->map(function (Column $column) use (&$data) {
616
            $data = $column->fill($data);
617
618
            $this->columnNames[] = $column->getName();
619
        });
620
621
        $this->buildRows($data);
622
623
        $this->builded = true;
624
    }
625
626
    /**
627
     * Disable header tools.
628
     *
629
     * @return $this
630
     */
631
    public function disableTools(bool $disable = true)
632
    {
633
        return $this->option('show_tools', !$disable);
634
    }
635
636
    /**
637
     * Disable grid filter.
638
     *
639
     * @return $this
640
     */
641
    public function disableFilter(bool $disable = true)
642
    {
643
        $this->tools->disableFilterButton($disable);
644
645
        return $this->option('show_filter', !$disable);
646
    }
647
648
    /**
649
     * Get filter of Grid.
650
     *
651
     * @return Filter
652
     */
653
    public function getFilter()
654
    {
655
        return $this->filter;
656
    }
657
658
    /**
659
     * Process the grid filter.
660
     *
661
     * @param bool $toArray
662
     *
663
     * @return array|Collection|mixed
664
     */
665
    public function processFilter($toArray = true)
666
    {
667
        if ($this->builder) {
668
            call_user_func($this->builder, $this);
669
        }
670
671
        return $this->filter->execute($toArray);
672
    }
673
674
    /**
675
     * Set the grid filter.
676
     *
677
     * @param Closure $callback
678
     */
679
    public function filter(Closure $callback)
680
    {
681
        call_user_func($callback, $this->filter);
682
    }
683
684
    /**
685
     * Render the grid filter.
686
     *
687
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
688
     */
689
    public function renderFilter()
690
    {
691
        if (!$this->option('show_filter')) {
692
            return '';
693
        }
694
695
        return $this->filter->render();
696
    }
697
698
    /**
699
     * Expand filter.
700
     *
701
     * @return $this
702
     */
703
    public function expandFilter()
704
    {
705
        $this->filter->expand();
706
707
        return $this;
708
    }
709
710
    /**
711
     * Build the grid rows.
712
     *
713
     * @param array $data
714
     *
715
     * @return void
716
     */
717
    protected function buildRows(array $data)
718
    {
719
        $this->rows = collect($data)->map(function ($model, $number) {
720
            return new Row($number, $model);
721
        });
722
723
        if ($this->rowsCallback) {
724
            $this->rows->map($this->rowsCallback);
725
        }
726
    }
727
728
    /**
729
     * Set grid row callback function.
730
     *
731
     * @param Closure $callable
732
     *
733
     * @return Collection|null
734
     */
735
    public function rows(Closure $callable = null)
736
    {
737
        if (is_null($callable)) {
738
            return $this->rows;
739
        }
740
741
        $this->rowsCallback = $callable;
742
    }
743
744
    /**
745
     * Setup grid tools.
746
     *
747
     * @param Closure $callback
748
     *
749
     * @return void
750
     */
751
    public function tools(Closure $callback)
752
    {
753
        call_user_func($callback, $this->tools);
754
    }
755
756
    /**
757
     * Render custom tools.
758
     *
759
     * @return string
760
     */
761
    public function renderHeaderTools()
762
    {
763
        return $this->tools->render();
764
    }
765
766
    /**
767
     * Set exporter driver for Grid to export.
768
     *
769
     * @param $exporter
770
     *
771
     * @return $this
772
     */
773
    public function exporter($exporter)
774
    {
775
        $this->exporter = $exporter;
776
777
        return $this;
778
    }
779
780
    /**
781
     * Get the export url.
782
     *
783
     * @param int  $scope
784
     * @param null $args
785
     *
786
     * @return string
787
     */
788
    public function getExportUrl($scope = 1, $args = null)
789
    {
790
        $input = array_merge(Input::all(), Exporter::formatExportQuery($scope, $args));
791
792
        if ($constraints = $this->model()->getConstraints()) {
793
            $input = array_merge($input, $constraints);
794
        }
795
796
        return $this->resource().'?'.http_build_query($input);
797
    }
798
799
    /**
800
     * Get create url.
801
     *
802
     * @return string
803
     */
804
    public function getCreateUrl()
805
    {
806
        $queryString = '';
807
808
        if ($constraints = $this->model()->getConstraints()) {
809
            $queryString = http_build_query($constraints);
810
        }
811
812
        return sprintf('%s/create%s',
813
            $this->resource(),
814
            $queryString ? ('?'.$queryString) : ''
815
        );
816
    }
817
818
    /**
819
     * If grid show header tools.
820
     *
821
     * @return bool
822
     */
823
    public function showTools()
824
    {
825
        return $this->option('show_tools');
826
    }
827
828
    /**
829
     * If grid show export btn.
830
     *
831
     * @return bool
832
     */
833
    public function showExportBtn()
834
    {
835
        return $this->option('show_exporter');
836
    }
837
838
    /**
839
     * Disable export.
840
     *
841
     * @return $this
842
     */
843
    public function disableExport(bool $disable = true)
844
    {
845
        return $this->option('show_exporter', !$disable);
846
    }
847
848
    /**
849
     * Render export button.
850
     *
851
     * @return string
852
     */
853
    public function renderExportButton()
854
    {
855
        return (new Tools\ExportButton($this))->render();
856
    }
857
858
    /**
859
     * Alias for method `disableCreateButton`.
860
     *
861
     * @return $this
862
     *
863
     * @deprecated
864
     */
865
    public function disableCreation()
866
    {
867
        return $this->disableCreateButton();
868
    }
869
870
    /**
871
     * Remove create button on grid.
872
     *
873
     * @return $this
874
     */
875
    public function disableCreateButton(bool $disable = true)
876
    {
877
        return $this->option('show_create_btn', !$disable);
878
    }
879
880
    /**
881
     * If allow creation.
882
     *
883
     * @return bool
884
     */
885
    public function showCreateBtn()
886
    {
887
        return $this->option('show_create_btn');
888
    }
889
890
    /**
891
     * Render create button for grid.
892
     *
893
     * @return string
894
     */
895
    public function renderCreateButton()
896
    {
897
        return (new Tools\CreateButton($this))->render();
898
    }
899
900
    /**
901
     * Remove column selector on grid.
902
     *
903
     * @param bool $disable
904
     * @return Grid|mixed
905
     */
906
    public function disableColumnSelector(bool $disable = true)
907
    {
908
        return $this->option('show_column_selector', !$disable);
909
    }
910
911
    /**
912
     * @return bool
913
     */
914
    public function showColumnSelector()
915
    {
916
        return $this->option('show_column_selector');
917
    }
918
919
    /**
920
     * @return string
921
     */
922
    public function renderColumnSelector()
923
    {
924
        return (new Grid\Tools\ColumnSelector($this))->render();
925
    }
926
927
    /**
928
     * Set grid header.
929
     *
930
     * @param Closure|null $closure
931
     *
932
     * @return $this|Closure
933
     */
934
    public function header(Closure $closure = null)
935
    {
936
        if (!$closure) {
937
            return $this->header;
938
        }
939
940
        $this->header = $closure;
941
942
        return $this;
943
    }
944
945
    /**
946
     * Render grid header.
947
     *
948
     * @return Tools\Header|string
949
     */
950
    public function renderHeader()
951
    {
952
        if (!$this->header) {
953
            return '';
954
        }
955
956
        return (new Tools\Header($this))->render();
957
    }
958
959
    /**
960
     * Set grid footer.
961
     *
962
     * @param Closure|null $closure
963
     *
964
     * @return $this|Closure
965
     */
966
    public function footer(Closure $closure = null)
967
    {
968
        if (!$closure) {
969
            return $this->footer;
970
        }
971
972
        $this->footer = $closure;
973
974
        return $this;
975
    }
976
977
    /**
978
     * Render grid footer.
979
     *
980
     * @return Tools\Footer|string
981
     */
982
    public function renderFooter()
983
    {
984
        if (!$this->footer) {
985
            return '';
986
        }
987
988
        return (new Tools\Footer($this))->render();
989
    }
990
991
    /**
992
     * Get current resource uri.
993
     *
994
     * @param string $path
995
     *
996
     * @return string
997
     */
998
    public function resource($path = null)
999
    {
1000
        if (!empty($path)) {
1001
            $this->resourcePath = $path;
1002
1003
            return $this;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this; (Encore\Admin\Grid) is incompatible with the return type documented by Encore\Admin\Grid::resource of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
1004
        }
1005
1006
        if (!empty($this->resourcePath)) {
1007
            return $this->resourcePath;
1008
        }
1009
1010
        return app('request')->getPathInfo();
1011
    }
1012
1013
    /**
1014
     * Handle get mutator column for grid.
1015
     *
1016
     * @param string $method
1017
     * @param string $label
1018
     *
1019
     * @return bool|Column
1020
     */
1021
    protected function handleGetMutatorColumn($method, $label)
1022
    {
1023
        if ($this->model()->eloquent()->hasGetMutator($method)) {
1024
            return $this->addColumn($method, $label);
1025
        }
1026
1027
        return false;
1028
    }
1029
1030
    /**
1031
     * Handle relation column for grid.
1032
     *
1033
     * @param string $method
1034
     * @param string $label
1035
     *
1036
     * @return bool|Column
1037
     */
1038
    protected function handleRelationColumn($method, $label)
1039
    {
1040
        $model = $this->model()->eloquent();
1041
1042
        if (!method_exists($model, $method)) {
1043
            return false;
1044
        }
1045
1046
        if (!($relation = $model->$method()) instanceof Relations\Relation) {
1047
            return false;
1048
        }
1049
1050
        if ($relation instanceof Relations\HasOne ||
1051
            $relation instanceof Relations\BelongsTo ||
1052
            $relation instanceof Relations\MorphOne
1053
        ) {
1054
            $this->model()->with($method);
1055
1056
            return $this->addColumn($method, $label)->setRelation(snake_case($method));
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated with message: Str::snake() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1057
        }
1058
1059
        if ($relation instanceof Relations\HasMany
1060
            || $relation instanceof Relations\BelongsToMany
1061
            || $relation instanceof Relations\MorphToMany
1062
        ) {
1063
            $this->model()->with($method);
1064
1065
            return $this->addColumn(snake_case($method), $label);
0 ignored issues
show
Deprecated Code introduced by
The function snake_case() has been deprecated with message: Str::snake() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1066
        }
1067
1068
        return false;
1069
    }
1070
1071
    /**
1072
     * Dynamically add columns to the grid view.
1073
     *
1074
     * @param $method
1075
     * @param $arguments
1076
     *
1077
     * @return Column
1078
     */
1079
    public function __call($method, $arguments)
1080
    {
1081
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($method);
1082
1083
        if ($this->model()->eloquent() instanceof MongodbModel) {
0 ignored issues
show
Bug introduced by
The class Jenssegers\Mongodb\Eloquent\Model does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
1084
            return $this->addColumn($method, $label);
1085
        }
1086
1087
        if ($column = $this->handleGetMutatorColumn($method, $label)) {
1088
            return $column;
1089
        }
1090
1091
        if ($column = $this->handleRelationColumn($method, $label)) {
1092
            return $column;
1093
        }
1094
1095
        return $this->addColumn($method, $label);
1096
    }
1097
1098
    /**
1099
     * Register column displayers.
1100
     *
1101
     * @return void.
0 ignored issues
show
Documentation introduced by
The doc-type void. could not be parsed: Unknown type name "void." at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1102
     */
1103
    public static function registerColumnDisplayer()
1104
    {
1105
        $map = [
1106
            'editable'    => Displayers\Editable::class,
1107
            'switch'      => Displayers\SwitchDisplay::class,
1108
            'switchGroup' => Displayers\SwitchGroup::class,
1109
            'select'      => Displayers\Select::class,
1110
            'image'       => Displayers\Image::class,
1111
            'label'       => Displayers\Label::class,
1112
            'button'      => Displayers\Button::class,
1113
            'link'        => Displayers\Link::class,
1114
            'badge'       => Displayers\Badge::class,
1115
            'progressBar' => Displayers\ProgressBar::class,
1116
            'radio'       => Displayers\Radio::class,
1117
            'checkbox'    => Displayers\Checkbox::class,
1118
            'orderable'   => Displayers\Orderable::class,
1119
            'table'       => Displayers\Table::class,
1120
            'expand'      => Displayers\Expand::class,
1121
            'modal'       => Displayers\Modal::class,
1122
        ];
1123
1124
        foreach ($map as $abstract => $class) {
1125
            Column::extend($abstract, $class);
1126
        }
1127
    }
1128
1129
    /**
1130
     * Add variables to grid view.
1131
     *
1132
     * @param array $variables
1133
     *
1134
     * @return $this
1135
     */
1136
    public function with($variables = [])
1137
    {
1138
        $this->variables = $variables;
1139
1140
        return $this;
1141
    }
1142
1143
    /**
1144
     * Get all variables will used in grid view.
1145
     *
1146
     * @return array
1147
     */
1148
    protected function variables()
1149
    {
1150
        $this->variables['grid'] = $this;
1151
1152
        return $this->variables;
1153
    }
1154
1155
    /**
1156
     * Set a view to render.
1157
     *
1158
     * @param string $view
1159
     * @param array  $variables
1160
     */
1161
    public function setView($view, $variables = [])
1162
    {
1163
        if (!empty($variables)) {
1164
            $this->with($variables);
1165
        }
1166
1167
        $this->view = $view;
1168
    }
1169
1170
    /**
1171
     * Set grid title.
1172
     *
1173
     * @param string $title
1174
     *
1175
     * @return $this
1176
     */
1177
    public function setTitle($title)
1178
    {
1179
        $this->variables['title'] = $title;
1180
1181
        return $this;
1182
    }
1183
1184
    /**
1185
     * Set relation for grid.
1186
     *
1187
     * @param Relations\Relation $relation
1188
     *
1189
     * @return $this
1190
     */
1191
    public function setRelation(Relations\Relation $relation)
1192
    {
1193
        $this->model()->setRelation($relation);
1194
1195
        return $this;
1196
    }
1197
1198
    /**
1199
     * Set resource path for grid.
1200
     *
1201
     * @param string $path
1202
     *
1203
     * @return $this
1204
     */
1205
    public function setResource($path)
1206
    {
1207
        $this->resourcePath = $path;
1208
1209
        return $this;
1210
    }
1211
1212
    /**
1213
     * Get the string contents of the grid view.
1214
     *
1215
     * @return string
1216
     */
1217
    public function render()
1218
    {
1219
        $this->handleExportRequest(true);
1220
1221
        try {
1222
            $this->build();
1223
        } catch (\Exception $e) {
1224
            return Handler::renderException($e);
1225
        }
1226
1227
        return view($this->view, $this->variables())->render();
0 ignored issues
show
Bug introduced by
The method render 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...
1228
    }
1229
}
1230