Completed
Push — master ( fbfcb9...4fd533 )
by Song
02:36
created

Grid::addRelationColumn()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 2
nop 2
dl 0
loc 20
rs 9.6
c 0
b 0
f 0
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\Concerns;
9
use Encore\Admin\Grid\Displayers;
10
use Encore\Admin\Grid\Exporter;
11
use Encore\Admin\Grid\Exporters\AbstractExporter;
12
use Encore\Admin\Grid\Model;
13
use Encore\Admin\Grid\Row;
14
use Encore\Admin\Grid\Tools;
15
use Illuminate\Database\Eloquent\Model as Eloquent;
16
use Illuminate\Database\Eloquent\Relations;
17
use Illuminate\Support\Collection;
18
use Illuminate\Support\Facades\Input;
19
use Illuminate\Support\Str;
20
use Jenssegers\Mongodb\Eloquent\Model as MongodbModel;
21
22
class Grid
23
{
24
    use Concerns\HasElementNames,
25
        Concerns\HasHeader,
26
        Concerns\HasFooter,
27
        Concerns\HasFilter,
28
        Concerns\HasTools,
29
        Concerns\HasTotalRow;
30
31
    /**
32
     * The grid data model instance.
33
     *
34
     * @var \Encore\Admin\Grid\Model
35
     */
36
    protected $model;
37
38
    /**
39
     * Collection of all grid columns.
40
     *
41
     * @var \Illuminate\Support\Collection
42
     */
43
    protected $columns;
44
45
    /**
46
     * Collection of all data rows.
47
     *
48
     * @var \Illuminate\Support\Collection
49
     */
50
    protected $rows;
51
52
    /**
53
     * Rows callable fucntion.
54
     *
55
     * @var \Closure
56
     */
57
    protected $rowsCallback;
58
59
    /**
60
     * All column names of the grid.
61
     *
62
     * @var array
63
     */
64
    public $columnNames = [];
65
66
    /**
67
     * Grid builder.
68
     *
69
     * @var \Closure
70
     */
71
    protected $builder;
72
73
    /**
74
     * Mark if the grid is builded.
75
     *
76
     * @var bool
77
     */
78
    protected $builded = false;
79
80
    /**
81
     * All variables in grid view.
82
     *
83
     * @var array
84
     */
85
    protected $variables = [];
86
87
    /**
88
     * Resource path of the grid.
89
     *
90
     * @var
91
     */
92
    protected $resourcePath;
93
94
    /**
95
     * Default primary key name.
96
     *
97
     * @var string
98
     */
99
    protected $keyName = 'id';
100
101
    /**
102
     * Export driver.
103
     *
104
     * @var string
105
     */
106
    protected $exporter;
107
108
    /**
109
     * View for grid to render.
110
     *
111
     * @var string
112
     */
113
    protected $view = 'admin::grid.table';
114
115
    /**
116
     * Per-page options.
117
     *
118
     * @var array
119
     */
120
    public $perPages = [10, 20, 30, 50, 100];
121
122
    /**
123
     * Default items count per-page.
124
     *
125
     * @var int
126
     */
127
    public $perPage = 20;
128
129
    /**
130
     * Callback for grid actions.
131
     *
132
     * @var Closure
133
     */
134
    protected $actionsCallback;
135
136
    /**
137
     * Actions column display class.
138
     *
139
     * @var string
140
     */
141
    protected $actionsClass = Displayers\Actions::class;
142
143
    /**
144
     * Options for grid.
145
     *
146
     * @var array
147
     */
148
    protected $options = [
149
        'show_pagination'        => true,
150
        'show_tools'             => true,
151
        'show_filter'            => true,
152
        'show_exporter'          => true,
153
        'show_actions'           => true,
154
        'show_row_selector'      => true,
155
        'show_create_btn'        => true,
156
        'show_column_selector'   => true,
157
    ];
158
159
    /**
160
     * @var string
161
     */
162
    public $tableID;
163
164
    /**
165
     * Initialization closure array.
166
     *
167
     * @var []Closure
168
     */
169
    protected static $initCallbacks = [];
170
171
    /**
172
     * Create a new grid instance.
173
     *
174
     * @param Eloquent $model
175
     * @param Closure  $builder
176
     */
177
    public function __construct(Eloquent $model, Closure $builder = null)
178
    {
179
        $this->keyName = $model->getKeyName();
180
        $this->model = new Model($model);
181
        $this->columns = new Collection();
182
        $this->rows = new Collection();
183
        $this->builder = $builder;
184
        $this->tableID = uniqid('grid-table');
185
186
        $this->model()->setGrid($this);
187
188
        $this->setupTools();
189
        $this->setupFilter();
190
191
        $this->handleExportRequest();
192
193
        $this->callInitCallbacks();
194
    }
195
196
    /**
197
     * Initialize with user pre-defined default disables and exporter, etc.
198
     *
199
     * @param Closure $callback
200
     */
201
    public static function init(Closure $callback = null)
202
    {
203
        static::$initCallbacks[] = $callback;
204
    }
205
206
    /**
207
     * Call the initialization closure array in sequence.
208
     */
209
    protected function callInitCallbacks()
210
    {
211
        if (empty(static::$initCallbacks)) {
212
            return;
213
        }
214
215
        foreach (static::$initCallbacks as $callback) {
216
            call_user_func($callback, $this);
217
        }
218
    }
219
220
    /**
221
     * Handle export request.
222
     *
223
     * @param bool $forceExport
224
     */
225
    protected function handleExportRequest($forceExport = false)
226
    {
227
        if (!$scope = request(Exporter::$queryName)) {
228
            return;
229
        }
230
231
        // clear output buffer.
232
        if (ob_get_length()) {
233
            ob_end_clean();
234
        }
235
236
        $this->model()->usePaginate(false);
237
238
        if ($this->builder) {
239
            call_user_func($this->builder, $this);
240
241
            $this->getExporter($scope)->export();
242
        }
243
244
        if ($forceExport) {
245
            $this->getExporter($scope)->export();
246
        }
247
    }
248
249
    /**
250
     * @param string $scope
251
     *
252
     * @return AbstractExporter
253
     */
254
    protected function getExporter($scope)
255
    {
256
        return (new Exporter($this))->resolve($this->exporter)->withScope($scope);
257
    }
258
259
    /**
260
     * Get or set option for grid.
261
     *
262
     * @param string $key
263
     * @param mixed  $value
264
     *
265
     * @return $this|mixed
266
     */
267
    public function option($key, $value = null)
268
    {
269
        if (is_null($value)) {
270
            return $this->options[$key];
271
        }
272
273
        $this->options[$key] = $value;
274
275
        return $this;
276
    }
277
278
    /**
279
     * Get primary key name of model.
280
     *
281
     * @return string
282
     */
283
    public function getKeyName()
284
    {
285
        return $this->keyName ?: 'id';
286
    }
287
288
    /**
289
     * Add a column to Grid.
290
     *
291
     * @param string $name
292
     * @param string $label
293
     *
294
     * @return Column
295
     */
296
    public function column($name, $label = '')
297
    {
298
        if (Str::contains($name, '.')) {
299
            return $this->addRelationColumn($name, $label);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->addRelationColumn($name, $label); of type Encore\Admin\Grid|Encore\Admin\Grid\Column adds the type Encore\Admin\Grid to the return on line 299 which is incompatible with the return type documented by Encore\Admin\Grid::column of type Encore\Admin\Grid\Column.
Loading history...
300
        }
301
302
        return $this->__call($name, [$label]);
303
    }
304
305
    /**
306
     * Batch add column to grid.
307
     *
308
     * @example
309
     * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]);
310
     * 2.$grid->columns('name', 'email' ...)
311
     *
312
     * @param array $columns
313
     *
314
     * @return Collection|null
315
     */
316
    public function columns($columns = [])
317
    {
318
        if (func_num_args() == 0) {
319
            return $this->columns;
320
        }
321
322
        if (func_num_args() == 1 && is_array($columns)) {
323
            foreach ($columns as $column => $label) {
324
                $this->column($column, $label);
325
            }
326
327
            return;
328
        }
329
330
        foreach (func_get_args() as $column) {
331
            $this->column($column);
332
        }
333
    }
334
335
    /**
336
     * Get all visible column instances.
337
     *
338
     * @return Collection|static
339
     */
340 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...
341
    {
342
        $visible = array_filter(explode(',', request(Tools\ColumnSelector::SELECT_COLUMN_NAME)));
343
344
        if (empty($visible)) {
345
            return $this->columns;
346
        }
347
348
        array_push($visible, '__row_selector__', '__actions__');
349
350
        return $this->columns->filter(function (Column $column) use ($visible) {
351
            return in_array($column->getName(), $visible);
352
        });
353
    }
354
355
    /**
356
     * Get all visible column names.
357
     *
358
     * @return array|static
359
     */
360 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...
361
    {
362
        $visible = array_filter(explode(',', request(Tools\ColumnSelector::SELECT_COLUMN_NAME)));
363
364
        if (empty($visible)) {
365
            return $this->columnNames;
366
        }
367
368
        array_push($visible, '__row_selector__', '__actions__');
369
370
        return collect($this->columnNames)->filter(function ($column) use ($visible) {
371
            return in_array($column, $visible);
372
        });
373
    }
374
375
    /**
376
     * Add column to grid.
377
     *
378
     * @param string $column
379
     * @param string $label
380
     *
381
     * @return Column
382
     */
383 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...
384
    {
385
        $column = new Column($column, $label);
386
        $column->setGrid($this);
387
388
        return tap($column, function ($value) {
389
            $this->columns->push($value);
390
        });
391
    }
392
393
    /**
394
     * Add a relation column to grid.
395
     *
396
     * @param string $name
397
     * @param string $label
398
     *
399
     * @return $this|bool|Column
400
     */
401
    protected function addRelationColumn($name, $label = '')
402
    {
403
        list($relation, $column) = explode('.', $name);
404
405
        $model = $this->model()->eloquent();
406
407
        if (!method_exists($model, $relation) || !$model->{$relation}() instanceof Relations\Relation) {
408
            $class = get_class($model);
409
410
            admin_error("Call to undefined relationship [{$relation}] on model [{$class}].");
411
412
            return $this;
413
        }
414
415
        $name = Str::snake($relation).'.'.$column;
416
417
        $this->model()->with($relation);
418
419
        return $this->addColumn($name, $label ?: ucfirst($column))->setRelation($relation, $column);
420
    }
421
422
    /**
423
     * Prepend column to grid.
424
     *
425
     * @param string $column
426
     * @param string $label
427
     *
428
     * @return Column
429
     */
430 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...
431
    {
432
        $column = new Column($column, $label);
433
        $column->setGrid($this);
434
435
        return tap($column, function ($value) {
436
            $this->columns->prepend($value);
437
        });
438
    }
439
440
    /**
441
     * Get Grid model.
442
     *
443
     * @return Model
444
     */
445
    public function model()
446
    {
447
        return $this->model;
448
    }
449
450
    /**
451
     * Paginate the grid.
452
     *
453
     * @param int $perPage
454
     *
455
     * @return void
456
     */
457
    public function paginate($perPage = 20)
458
    {
459
        $this->perPage = $perPage;
460
461
        $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...
462
    }
463
464
    /**
465
     * Get the grid paginator.
466
     *
467
     * @return mixed
468
     */
469
    public function paginator()
470
    {
471
        return new Tools\Paginator($this);
472
    }
473
474
    /**
475
     * Disable grid pagination.
476
     *
477
     * @return $this
478
     */
479
    public function disablePagination(bool $disable = true)
480
    {
481
        $this->model->usePaginate(!$disable);
482
483
        return $this->option('show_pagination', !$disable);
484
    }
485
486
    /**
487
     * If this grid use pagination.
488
     *
489
     * @return bool
490
     */
491
    public function showPagination()
492
    {
493
        return $this->option('show_pagination');
494
    }
495
496
    /**
497
     * Set per-page options.
498
     *
499
     * @param array $perPages
500
     */
501
    public function perPages(array $perPages)
502
    {
503
        $this->perPages = $perPages;
504
    }
505
506
    /**
507
     * Disable all actions.
508
     *
509
     * @return $this
510
     */
511
    public function disableActions(bool $disable = true)
512
    {
513
        return $this->option('show_actions', !$disable);
514
    }
515
516
    /**
517
     * Set grid action callback.
518
     *
519
     * @param Closure|string $actions
520
     *
521
     * @return $this
522
     */
523
    public function actions($actions)
524
    {
525
        if ($actions instanceof Closure) {
526
            $this->actionsCallback = $actions;
527
        }
528
529
        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...
530
            $this->actionsClass = $actions;
531
        }
532
533
        return $this;
534
    }
535
536
    /**
537
     * Add `actions` column for grid.
538
     *
539
     * @return void
540
     */
541
    protected function appendActionsColumn()
542
    {
543
        if (!$this->option('show_actions')) {
544
            return;
545
        }
546
547
        $this->addColumn('__actions__', trans('admin.action'))
548
            ->displayUsing($this->actionsClass, [$this->actionsCallback]);
549
    }
550
551
    /**
552
     * Disable row selector.
553
     *
554
     * @return Grid|mixed
555
     */
556
    public function disableRowSelector(bool $disable = true)
557
    {
558
        $this->tools->disableBatchActions($disable);
559
560
        return $this->option('show_row_selector', !$disable);
561
    }
562
563
    /**
564
     * Prepend checkbox column for grid.
565
     *
566
     * @return void
567
     */
568
    protected function prependRowSelectorColumn()
569
    {
570
        if (!$this->option('show_row_selector')) {
571
            return;
572
        }
573
574
        $this->prependColumn(Column::SELECT_COLUMN_NAME, ' ')
575
            ->displayUsing(Displayers\RowSelector::class);
576
    }
577
578
    /**
579
     * Build the grid.
580
     *
581
     * @return void
582
     */
583
    public function build()
584
    {
585
        if ($this->builded) {
586
            return;
587
        }
588
589
        $collection = $this->processFilter(false);
590
591
        $data = $collection->toArray();
592
593
        $this->prependRowSelectorColumn();
594
        $this->appendActionsColumn();
595
596
        Column::setOriginalGridModels($collection);
597
598
        $this->columns->map(function (Column $column) use (&$data) {
599
            $data = $column->fill($data);
600
601
            $this->columnNames[] = $column->getName();
602
        });
603
604
        $this->buildRows($data);
605
606
        $this->builded = true;
607
    }
608
609
    /**
610
     * Build the grid rows.
611
     *
612
     * @param array $data
613
     *
614
     * @return void
615
     */
616
    protected function buildRows(array $data)
617
    {
618
        $this->rows = collect($data)->map(function ($model, $number) {
619
            return new Row($number, $model);
620
        });
621
622
        if ($this->rowsCallback) {
623
            $this->rows->map($this->rowsCallback);
624
        }
625
    }
626
627
    /**
628
     * Set grid row callback function.
629
     *
630
     * @param Closure $callable
631
     *
632
     * @return Collection|null
633
     */
634
    public function rows(Closure $callable = null)
635
    {
636
        if (is_null($callable)) {
637
            return $this->rows;
638
        }
639
640
        $this->rowsCallback = $callable;
641
    }
642
643
    /**
644
     * Set exporter driver for Grid to export.
645
     *
646
     * @param $exporter
647
     *
648
     * @return $this
649
     */
650
    public function exporter($exporter)
651
    {
652
        $this->exporter = $exporter;
653
654
        return $this;
655
    }
656
657
    /**
658
     * Get the export url.
659
     *
660
     * @param int  $scope
661
     * @param null $args
662
     *
663
     * @return string
664
     */
665
    public function getExportUrl($scope = 1, $args = null)
666
    {
667
        $input = array_merge(Input::all(), Exporter::formatExportQuery($scope, $args));
668
669
        if ($constraints = $this->model()->getConstraints()) {
670
            $input = array_merge($input, $constraints);
671
        }
672
673
        return $this->resource().'?'.http_build_query($input);
674
    }
675
676
    /**
677
     * Get create url.
678
     *
679
     * @return string
680
     */
681
    public function getCreateUrl()
682
    {
683
        $queryString = '';
684
685
        if ($constraints = $this->model()->getConstraints()) {
686
            $queryString = http_build_query($constraints);
687
        }
688
689
        return sprintf('%s/create%s',
690
            $this->resource(),
691
            $queryString ? ('?'.$queryString) : ''
692
        );
693
    }
694
695
    /**
696
     * If grid show export btn.
697
     *
698
     * @return bool
699
     */
700
    public function showExportBtn()
701
    {
702
        return $this->option('show_exporter');
703
    }
704
705
    /**
706
     * Disable export.
707
     *
708
     * @return $this
709
     */
710
    public function disableExport(bool $disable = true)
711
    {
712
        return $this->option('show_exporter', !$disable);
713
    }
714
715
    /**
716
     * Render export button.
717
     *
718
     * @return string
719
     */
720
    public function renderExportButton()
721
    {
722
        return (new Tools\ExportButton($this))->render();
723
    }
724
725
    /**
726
     * Alias for method `disableCreateButton`.
727
     *
728
     * @return $this
729
     *
730
     * @deprecated
731
     */
732
    public function disableCreation()
733
    {
734
        return $this->disableCreateButton();
735
    }
736
737
    /**
738
     * Remove create button on grid.
739
     *
740
     * @return $this
741
     */
742
    public function disableCreateButton(bool $disable = true)
743
    {
744
        return $this->option('show_create_btn', !$disable);
745
    }
746
747
    /**
748
     * If allow creation.
749
     *
750
     * @return bool
751
     */
752
    public function showCreateBtn()
753
    {
754
        return $this->option('show_create_btn');
755
    }
756
757
    /**
758
     * Render create button for grid.
759
     *
760
     * @return string
761
     */
762
    public function renderCreateButton()
763
    {
764
        return (new Tools\CreateButton($this))->render();
765
    }
766
767
    /**
768
     * Remove column selector on grid.
769
     *
770
     * @param bool $disable
771
     *
772
     * @return Grid|mixed
773
     */
774
    public function disableColumnSelector(bool $disable = true)
775
    {
776
        return $this->option('show_column_selector', !$disable);
777
    }
778
779
    /**
780
     * @return bool
781
     */
782
    public function showColumnSelector()
783
    {
784
        return $this->option('show_column_selector');
785
    }
786
787
    /**
788
     * @return string
789
     */
790
    public function renderColumnSelector()
791
    {
792
        return (new Grid\Tools\ColumnSelector($this))->render();
793
    }
794
795
    /**
796
     * Get current resource uri.
797
     *
798
     * @param string $path
799
     *
800
     * @return string
801
     */
802
    public function resource($path = null)
803
    {
804
        if (!empty($path)) {
805
            $this->resourcePath = $path;
806
807
            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...
808
        }
809
810
        if (!empty($this->resourcePath)) {
811
            return $this->resourcePath;
812
        }
813
814
        return app('request')->getPathInfo();
815
    }
816
817
    /**
818
     * Handle get mutator column for grid.
819
     *
820
     * @param string $method
821
     * @param string $label
822
     *
823
     * @return bool|Column
824
     */
825
    protected function handleGetMutatorColumn($method, $label)
826
    {
827
        if ($this->model()->eloquent()->hasGetMutator($method)) {
828
            return $this->addColumn($method, $label);
829
        }
830
831
        return false;
832
    }
833
834
    /**
835
     * Handle relation column for grid.
836
     *
837
     * @param string $method
838
     * @param string $label
839
     *
840
     * @return bool|Column
841
     */
842
    protected function handleRelationColumn($method, $label)
843
    {
844
        $model = $this->model()->eloquent();
845
846
        if (!method_exists($model, $method)) {
847
            return false;
848
        }
849
850
        if (!($relation = $model->$method()) instanceof Relations\Relation) {
851
            return false;
852
        }
853
854
        if ($relation instanceof Relations\HasOne ||
855
            $relation instanceof Relations\BelongsTo ||
856
            $relation instanceof Relations\MorphOne
857
        ) {
858
            $this->model()->with($method);
859
860
            return $this->addColumn($method, $label)->setRelation(Str::snake($method));
861
        }
862
863
        if ($relation instanceof Relations\HasMany
864
            || $relation instanceof Relations\BelongsToMany
865
            || $relation instanceof Relations\MorphToMany
866
            || $relation instanceof Relations\HasManyThrough
867
        ) {
868
            $this->model()->with($method);
869
870
            return $this->addColumn(Str::snake($method), $label);
871
        }
872
873
        return false;
874
    }
875
876
    /**
877
     * Dynamically add columns to the grid view.
878
     *
879
     * @param $method
880
     * @param $arguments
881
     *
882
     * @return Column
883
     */
884
    public function __call($method, $arguments)
885
    {
886
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($method);
887
888
        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...
889
            return $this->addColumn($method, $label);
890
        }
891
892
        if ($column = $this->handleGetMutatorColumn($method, $label)) {
893
            return $column;
894
        }
895
896
        if ($column = $this->handleRelationColumn($method, $label)) {
897
            return $column;
898
        }
899
900
        return $this->addColumn($method, $label);
901
    }
902
903
    /**
904
     * Register column displayers.
905
     *
906
     * @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...
907
     */
908
    public static function registerColumnDisplayer()
909
    {
910
        $map = [
911
            'editable'    => Displayers\Editable::class,
912
            'switch'      => Displayers\SwitchDisplay::class,
913
            'switchGroup' => Displayers\SwitchGroup::class,
914
            'select'      => Displayers\Select::class,
915
            'image'       => Displayers\Image::class,
916
            'label'       => Displayers\Label::class,
917
            'button'      => Displayers\Button::class,
918
            'link'        => Displayers\Link::class,
919
            'badge'       => Displayers\Badge::class,
920
            'progressBar' => Displayers\ProgressBar::class,
921
            'radio'       => Displayers\Radio::class,
922
            'checkbox'    => Displayers\Checkbox::class,
923
            'orderable'   => Displayers\Orderable::class,
924
            'table'       => Displayers\Table::class,
925
            'expand'      => Displayers\Expand::class,
926
            'modal'       => Displayers\Modal::class,
927
        ];
928
929
        foreach ($map as $abstract => $class) {
930
            Column::extend($abstract, $class);
931
        }
932
    }
933
934
    /**
935
     * Add variables to grid view.
936
     *
937
     * @param array $variables
938
     *
939
     * @return $this
940
     */
941
    public function with($variables = [])
942
    {
943
        $this->variables = $variables;
944
945
        return $this;
946
    }
947
948
    /**
949
     * Get all variables will used in grid view.
950
     *
951
     * @return array
952
     */
953
    protected function variables()
954
    {
955
        $this->variables['grid'] = $this;
956
957
        return $this->variables;
958
    }
959
960
    /**
961
     * Set a view to render.
962
     *
963
     * @param string $view
964
     * @param array  $variables
965
     */
966
    public function setView($view, $variables = [])
967
    {
968
        if (!empty($variables)) {
969
            $this->with($variables);
970
        }
971
972
        $this->view = $view;
973
    }
974
975
    /**
976
     * Set grid title.
977
     *
978
     * @param string $title
979
     *
980
     * @return $this
981
     */
982
    public function setTitle($title)
983
    {
984
        $this->variables['title'] = $title;
985
986
        return $this;
987
    }
988
989
    /**
990
     * Set relation for grid.
991
     *
992
     * @param Relations\Relation $relation
993
     *
994
     * @return $this
995
     */
996
    public function setRelation(Relations\Relation $relation)
997
    {
998
        $this->model()->setRelation($relation);
999
1000
        return $this;
1001
    }
1002
1003
    /**
1004
     * Set resource path for grid.
1005
     *
1006
     * @param string $path
1007
     *
1008
     * @return $this
1009
     */
1010
    public function setResource($path)
1011
    {
1012
        $this->resourcePath = $path;
1013
1014
        return $this;
1015
    }
1016
1017
    /**
1018
     * Get the string contents of the grid view.
1019
     *
1020
     * @return string
1021
     */
1022
    public function render()
1023
    {
1024
        $this->handleExportRequest(true);
1025
1026
        try {
1027
            $this->build();
1028
        } catch (\Exception $e) {
1029
            return Handler::renderException($e);
1030
        }
1031
1032
        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...
1033
    }
1034
}
1035