Completed
Push — master ( 7a7fba...3207da )
by Song
14:14
created

Grid::visibleColumns()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 14

Duplication

Lines 14
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 0
dl 14
loc 14
rs 9.7998
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\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
     * Options for grid.
147
     *
148
     * @var array
149
     */
150
    protected $options = [
151
        'show_pagination'   => true,
152
        'show_tools'        => true,
153
        'show_filter'       => true,
154
        'show_exporter'     => true,
155
        'show_actions'      => true,
156
        'show_row_selector' => true,
157
        'show_create_btn'   => true,
158
    ];
159
160
    /**
161
     * @var Closure
162
     */
163
    protected $header;
164
165
    /**
166
     * @var Closure
167
     */
168
    protected $footer;
169
170
    /**
171
     * Create a new grid instance.
172
     *
173
     * @param Eloquent $model
174
     * @param Closure  $builder
175
     */
176
    public function __construct(Eloquent $model, Closure $builder = null)
177
    {
178
        $this->keyName = $model->getKeyName();
179
        $this->model = new Model($model);
180
        $this->columns = new Collection();
181
        $this->rows = new Collection();
182
        $this->builder = $builder;
183
184
        $this->model()->setGrid($this);
185
186
        $this->setupTools();
187
        $this->setupFilter();
188
189
        $this->handleExportRequest();
190
    }
191
192
    /**
193
     * Setup grid tools.
194
     */
195
    public function setupTools()
196
    {
197
        $this->tools = new Tools($this);
198
    }
199
200
    /**
201
     * Setup grid filter.
202
     *
203
     * @return void
204
     */
205
    protected function setupFilter()
206
    {
207
        $this->filter = new Filter($this->model());
208
    }
209
210
    /**
211
     * Handle export request.
212
     *
213
     * @param bool $forceExport
214
     */
215
    protected function handleExportRequest($forceExport = false)
216
    {
217
        if (!$scope = request(Exporter::$queryName)) {
218
            return;
219
        }
220
221
        // clear output buffer.
222
        if (ob_get_length()) {
223
            ob_end_clean();
224
        }
225
226
        $this->model()->usePaginate(false);
227
228
        if ($this->builder) {
229
            call_user_func($this->builder, $this);
230
231
            $this->getExporter($scope)->export();
232
        }
233
234
        if ($forceExport) {
235
            $this->getExporter($scope)->export();
236
        }
237
    }
238
239
    /**
240
     * @param string $scope
241
     *
242
     * @return AbstractExporter
243
     */
244
    protected function getExporter($scope)
245
    {
246
        return (new Exporter($this))->resolve($this->exporter)->withScope($scope);
247
    }
248
249
    /**
250
     * Get or set option for grid.
251
     *
252
     * @param string $key
253
     * @param mixed  $value
254
     *
255
     * @return $this|mixed
256
     */
257
    public function option($key, $value = null)
258
    {
259
        if (is_null($value)) {
260
            return $this->options[$key];
261
        }
262
263
        $this->options[$key] = $value;
264
265
        return $this;
266
    }
267
268
    /**
269
     * Get primary key name of model.
270
     *
271
     * @return string
272
     */
273
    public function getKeyName()
274
    {
275
        return $this->keyName ?: 'id';
276
    }
277
278
    /**
279
     * Add column to Grid.
280
     *
281
     * @param string $name
282
     * @param string $label
283
     *
284
     * @return Column
285
     */
286
    public function column($name, $label = '')
287
    {
288
        $relationName = $relationColumn = '';
289
290
        if (strpos($name, '.') !== false) {
291
            list($relationName, $relationColumn) = explode('.', $name);
292
293
            $relation = $this->model()->eloquent()->$relationName();
294
295
            $label = empty($label) ? ucfirst($relationColumn) : $label;
296
297
            $name = snake_case($relationName).'.'.$relationColumn;
298
        }
299
300
        $column = $this->addColumn($name, $label);
301
302
        if (isset($relation) && $relation instanceof Relations\Relation) {
303
            $this->model()->with($relationName);
304
            $column->setRelation($relationName, $relationColumn);
305
        }
306
307
        return $column;
308
    }
309
310
    /**
311
     * Batch add column to grid.
312
     *
313
     * @example
314
     * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]);
315
     * 2.$grid->columns('name', 'email' ...)
316
     *
317
     * @param array $columns
318
     *
319
     * @return Collection|null
320
     */
321
    public function columns($columns = [])
322
    {
323
        if (func_num_args() == 0) {
324
            return $this->columns;
325
        }
326
327
        if (func_num_args() == 1 && is_array($columns)) {
328
            foreach ($columns as $column => $label) {
329
                $this->column($column, $label);
330
            }
331
332
            return;
333
        }
334
335
        foreach (func_get_args() as $column) {
336
            $this->column($column);
337
        }
338
    }
339
340
    /**
341
     * Get all visible column instances.
342
     *
343
     * @return Collection|static
344
     */
345 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...
346
    {
347
        $visible = array_filter(explode(',', request(Tools\ColumnSelector::SELECT_COLUMN_NAME)));
348
349
        if (empty($visible)) {
350
            return $this->columns;
351
        }
352
353
        array_push($visible, '__row_selector__', '__actions__');
354
355
        return $this->columns->filter(function (Column $column) use ($visible) {
356
            return in_array($column->getName(), $visible);
357
        });
358
    }
359
360
    /**
361
     * Get all visible column names.
362
     *
363
     * @return array|static
364
     */
365 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...
366
    {
367
        $visible = array_filter(explode(',', request(Tools\ColumnSelector::SELECT_COLUMN_NAME)));
368
369
        if (empty($visible)) {
370
            return $this->columnNames;
371
        }
372
373
        array_push($visible, '__row_selector__', '__actions__');
374
375
        return collect($this->columnNames)->filter(function ($column) use ($visible) {
376
            return in_array($column, $visible);
377
        });
378
    }
379
380
    /**
381
     * Add column to grid.
382
     *
383
     * @param string $column
384
     * @param string $label
385
     *
386
     * @return Column
387
     */
388 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...
389
    {
390
        $column = new Column($column, $label);
391
        $column->setGrid($this);
392
393
        return tap($column, function ($value) {
394
            $this->columns->push($value);
395
        });
396
    }
397
398
    /**
399
     * Prepend column to grid.
400
     *
401
     * @param string $column
402
     * @param string $label
403
     *
404
     * @return Column
405
     */
406 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...
407
    {
408
        $column = new Column($column, $label);
409
        $column->setGrid($this);
410
411
        return tap($column, function ($value) {
412
            $this->columns->prepend($value);
413
        });
414
    }
415
416
    /**
417
     * Get Grid model.
418
     *
419
     * @return Model
420
     */
421
    public function model()
422
    {
423
        return $this->model;
424
    }
425
426
    /**
427
     * Paginate the grid.
428
     *
429
     * @param int $perPage
430
     *
431
     * @return void
432
     */
433
    public function paginate($perPage = 20)
434
    {
435
        $this->perPage = $perPage;
436
437
        $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...
438
    }
439
440
    /**
441
     * Get the grid paginator.
442
     *
443
     * @return mixed
444
     */
445
    public function paginator()
446
    {
447
        return new Tools\Paginator($this);
448
    }
449
450
    /**
451
     * Disable grid pagination.
452
     *
453
     * @return $this
454
     */
455
    public function disablePagination()
456
    {
457
        $this->model->usePaginate(false);
458
459
        $this->option('show_pagination', false);
460
461
        return $this;
462
    }
463
464
    /**
465
     * If this grid use pagination.
466
     *
467
     * @return bool
468
     */
469
    public function showPagination()
470
    {
471
        return $this->option('show_pagination');
472
    }
473
474
    /**
475
     * Set per-page options.
476
     *
477
     * @param array $perPages
478
     */
479
    public function perPages(array $perPages)
480
    {
481
        $this->perPages = $perPages;
482
    }
483
484
    /**
485
     * Disable all actions.
486
     *
487
     * @return $this
488
     */
489
    public function disableActions()
490
    {
491
        return $this->option('show_actions', false);
492
    }
493
494
    /**
495
     * Set grid action callback.
496
     *
497
     * @param Closure $callback
498
     *
499
     * @return $this
500
     */
501
    public function actions(Closure $callback)
502
    {
503
        $this->actionsCallback = $callback;
504
505
        return $this;
506
    }
507
508
    /**
509
     * Add `actions` column for grid.
510
     *
511
     * @return void
512
     */
513
    protected function appendActionsColumn()
514
    {
515
        if (!$this->option('show_actions')) {
516
            return;
517
        }
518
519
        $this->addColumn('__actions__', trans('admin.action'))
520
            ->displayUsing(Displayers\Actions::class, [$this->actionsCallback]);
521
    }
522
523
    /**
524
     * Disable row selector.
525
     *
526
     * @return Grid|mixed
527
     */
528
    public function disableRowSelector()
529
    {
530
        $this->tools(function ($tools) {
531
            /* @var Grid\Tools $tools */
532
            $tools->disableBatchActions();
533
        });
534
535
        return $this->option('show_row_selector', false);
536
    }
537
538
    /**
539
     * Prepend checkbox column for grid.
540
     *
541
     * @return void
542
     */
543
    protected function prependRowSelectorColumn()
544
    {
545
        if (!$this->option('show_row_selector')) {
546
            return;
547
        }
548
549
        $this->prependColumn(Column::SELECT_COLUMN_NAME, ' ')
550
            ->displayUsing(Displayers\RowSelector::class);
551
    }
552
553
    /**
554
     * Build the grid.
555
     *
556
     * @return void
557
     */
558
    public function build()
559
    {
560
        if ($this->builded) {
561
            return;
562
        }
563
564
        $collection = $this->processFilter(false);
565
566
        $data = $collection->toArray();
567
568
        $this->prependRowSelectorColumn();
569
        $this->appendActionsColumn();
570
571
        Column::setOriginalGridModels($collection);
572
573
        $this->columns->map(function (Column $column) use (&$data) {
574
            $data = $column->fill($data);
575
576
            $this->columnNames[] = $column->getName();
577
        });
578
579
        $this->buildRows($data);
580
581
        $this->builded = true;
582
    }
583
584
    /**
585
     * Disable header tools.
586
     *
587
     * @return $this
588
     */
589
    public function disableTools()
590
    {
591
        $this->option('show_tools', false);
592
593
        return $this;
594
    }
595
596
    /**
597
     * Disable grid filter.
598
     *
599
     * @return $this
600
     */
601
    public function disableFilter()
602
    {
603
        $this->option('show_filter', false);
604
605
        $this->tools->disableFilterButton();
606
607
        return $this;
608
    }
609
610
    /**
611
     * Get filter of Grid.
612
     *
613
     * @return Filter
614
     */
615
    public function getFilter()
616
    {
617
        return $this->filter;
618
    }
619
620
    /**
621
     * Process the grid filter.
622
     *
623
     * @param bool $toArray
624
     *
625
     * @return array|Collection|mixed
626
     */
627
    public function processFilter($toArray = true)
628
    {
629
        if ($this->builder) {
630
            call_user_func($this->builder, $this);
631
        }
632
633
        return $this->filter->execute($toArray);
634
    }
635
636
    /**
637
     * Set the grid filter.
638
     *
639
     * @param Closure $callback
640
     */
641
    public function filter(Closure $callback)
642
    {
643
        call_user_func($callback, $this->filter);
644
    }
645
646
    /**
647
     * Render the grid filter.
648
     *
649
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
650
     */
651
    public function renderFilter()
652
    {
653
        if (!$this->option('show_filter')) {
654
            return '';
655
        }
656
657
        return $this->filter->render();
658
    }
659
660
    /**
661
     * Expand filter.
662
     *
663
     * @return $this
664
     */
665
    public function expandFilter()
666
    {
667
        $this->filter->expand();
668
669
        return $this;
670
    }
671
672
    /**
673
     * Build the grid rows.
674
     *
675
     * @param array $data
676
     *
677
     * @return void
678
     */
679
    protected function buildRows(array $data)
680
    {
681
        $this->rows = collect($data)->map(function ($model, $number) {
682
            return new Row($number, $model);
683
        });
684
685
        if ($this->rowsCallback) {
686
            $this->rows->map($this->rowsCallback);
687
        }
688
    }
689
690
    /**
691
     * Set grid row callback function.
692
     *
693
     * @param Closure $callable
694
     *
695
     * @return Collection|null
696
     */
697
    public function rows(Closure $callable = null)
698
    {
699
        if (is_null($callable)) {
700
            return $this->rows;
701
        }
702
703
        $this->rowsCallback = $callable;
704
    }
705
706
    /**
707
     * Setup grid tools.
708
     *
709
     * @param Closure $callback
710
     *
711
     * @return void
712
     */
713
    public function tools(Closure $callback)
714
    {
715
        call_user_func($callback, $this->tools);
716
    }
717
718
    /**
719
     * Render custom tools.
720
     *
721
     * @return string
722
     */
723
    public function renderHeaderTools()
724
    {
725
        return $this->tools->render();
726
    }
727
728
    /**
729
     * Set exporter driver for Grid to export.
730
     *
731
     * @param $exporter
732
     *
733
     * @return $this
734
     */
735
    public function exporter($exporter)
736
    {
737
        $this->exporter = $exporter;
738
739
        return $this;
740
    }
741
742
    /**
743
     * Get the export url.
744
     *
745
     * @param int  $scope
746
     * @param null $args
747
     *
748
     * @return string
749
     */
750
    public function getExportUrl($scope = 1, $args = null)
751
    {
752
        $input = array_merge(Input::all(), Exporter::formatExportQuery($scope, $args));
753
754
        if ($constraints = $this->model()->getConstraints()) {
755
            $input = array_merge($input, $constraints);
756
        }
757
758
        return $this->resource().'?'.http_build_query($input);
759
    }
760
761
    /**
762
     * Get create url.
763
     *
764
     * @return string
765
     */
766
    public function getCreateUrl()
767
    {
768
        $queryString = '';
769
770
        if ($constraints = $this->model()->getConstraints()) {
771
            $queryString = http_build_query($constraints);
772
        }
773
774
        return sprintf('%s/create%s',
775
            $this->resource(),
776
            $queryString ? ('?'.$queryString) : ''
777
        );
778
    }
779
780
    /**
781
     * If grid show header tools.
782
     *
783
     * @return bool
784
     */
785
    public function showTools()
786
    {
787
        return $this->option('show_tools');
788
    }
789
790
    /**
791
     * If grid show export btn.
792
     *
793
     * @return bool
794
     */
795
    public function showExportBtn()
796
    {
797
        return $this->option('show_exporter');
798
    }
799
800
    /**
801
     * Disable export.
802
     *
803
     * @return $this
804
     */
805
    public function disableExport()
806
    {
807
        return $this->option('show_exporter', false);
808
    }
809
810
    /**
811
     * Render export button.
812
     *
813
     * @return string
814
     */
815
    public function renderExportButton()
816
    {
817
        return (new Tools\ExportButton($this))->render();
818
    }
819
820
    /**
821
     * Alias for method `disableCreateButton`.
822
     *
823
     * @return $this
824
     *
825
     * @deprecated
826
     */
827
    public function disableCreation()
828
    {
829
        return $this->disableCreateButton();
830
    }
831
832
    /**
833
     * Remove create button on grid.
834
     *
835
     * @return $this
836
     */
837
    public function disableCreateButton()
838
    {
839
        return $this->option('show_create_btn', false);
840
    }
841
842
    /**
843
     * If allow creation.
844
     *
845
     * @return bool
846
     */
847
    public function showCreateBtn()
848
    {
849
        return $this->option('show_create_btn');
850
    }
851
852
    /**
853
     * Render create button for grid.
854
     *
855
     * @return string
856
     */
857
    public function renderCreateButton()
858
    {
859
        return (new Tools\CreateButton($this))->render();
860
    }
861
862
    /**
863
     * @return string
864
     */
865
    public function renderColumnSelector()
866
    {
867
        return (new Grid\Tools\ColumnSelector($this))->render();
868
    }
869
870
    /**
871
     * Set grid header.
872
     *
873
     * @param Closure|null $closure
874
     *
875
     * @return $this|Closure
876
     */
877
    public function header(Closure $closure = null)
878
    {
879
        if (!$closure) {
880
            return $this->header;
881
        }
882
883
        $this->header = $closure;
884
885
        return $this;
886
    }
887
888
    /**
889
     * Render grid header.
890
     *
891
     * @return Tools\Header|string
892
     */
893
    public function renderHeader()
894
    {
895
        if (!$this->header) {
896
            return '';
897
        }
898
899
        return (new Tools\Header($this))->render();
900
    }
901
902
    /**
903
     * Set grid footer.
904
     *
905
     * @param Closure|null $closure
906
     *
907
     * @return $this|Closure
908
     */
909
    public function footer(Closure $closure = null)
910
    {
911
        if (!$closure) {
912
            return $this->footer;
913
        }
914
915
        $this->footer = $closure;
916
917
        return $this;
918
    }
919
920
    /**
921
     * Render grid footer.
922
     *
923
     * @return Tools\Footer|string
924
     */
925
    public function renderFooter()
926
    {
927
        if (!$this->footer) {
928
            return '';
929
        }
930
931
        return (new Tools\Footer($this))->render();
932
    }
933
934
    /**
935
     * Get current resource uri.
936
     *
937
     * @param string $path
938
     *
939
     * @return string
940
     */
941
    public function resource($path = null)
942
    {
943
        if (!empty($path)) {
944
            $this->resourcePath = $path;
945
946
            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...
947
        }
948
949
        if (!empty($this->resourcePath)) {
950
            return $this->resourcePath;
951
        }
952
953
        return app('request')->getPathInfo();
954
    }
955
956
    /**
957
     * Handle get mutator column for grid.
958
     *
959
     * @param string $method
960
     * @param string $label
961
     *
962
     * @return bool|Column
963
     */
964
    protected function handleGetMutatorColumn($method, $label)
965
    {
966
        if ($this->model()->eloquent()->hasGetMutator($method)) {
967
            return $this->addColumn($method, $label);
968
        }
969
970
        return false;
971
    }
972
973
    /**
974
     * Handle relation column for grid.
975
     *
976
     * @param string $method
977
     * @param string $label
978
     *
979
     * @return bool|Column
980
     */
981
    protected function handleRelationColumn($method, $label)
982
    {
983
        $model = $this->model()->eloquent();
984
985
        if (!method_exists($model, $method)) {
986
            return false;
987
        }
988
989
        if (!($relation = $model->$method()) instanceof Relations\Relation) {
990
            return false;
991
        }
992
993
        if ($relation instanceof Relations\HasOne ||
994
            $relation instanceof Relations\BelongsTo ||
995
            $relation instanceof Relations\MorphOne
996
        ) {
997
            $this->model()->with($method);
998
999
            return $this->addColumn($method, $label)->setRelation(snake_case($method));
1000
        }
1001
1002
        if ($relation instanceof Relations\HasMany
1003
            || $relation instanceof Relations\BelongsToMany
1004
            || $relation instanceof Relations\MorphToMany
1005
        ) {
1006
            $this->model()->with($method);
1007
1008
            return $this->addColumn(snake_case($method), $label);
1009
        }
1010
1011
        return false;
1012
    }
1013
1014
    /**
1015
     * Dynamically add columns to the grid view.
1016
     *
1017
     * @param $method
1018
     * @param $arguments
1019
     *
1020
     * @return Column
1021
     */
1022
    public function __call($method, $arguments)
1023
    {
1024
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($method);
1025
1026
        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...
1027
            return $this->addColumn($method, $label);
1028
        }
1029
1030
        if ($column = $this->handleGetMutatorColumn($method, $label)) {
1031
            return $column;
1032
        }
1033
1034
        if ($column = $this->handleRelationColumn($method, $label)) {
1035
            return $column;
1036
        }
1037
1038
        return $this->addColumn($method, $label);
1039
    }
1040
1041
    /**
1042
     * Register column displayers.
1043
     *
1044
     * @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...
1045
     */
1046
    public static function registerColumnDisplayer()
1047
    {
1048
        $map = [
1049
            'editable'    => Displayers\Editable::class,
1050
            'switch'      => Displayers\SwitchDisplay::class,
1051
            'switchGroup' => Displayers\SwitchGroup::class,
1052
            'select'      => Displayers\Select::class,
1053
            'image'       => Displayers\Image::class,
1054
            'label'       => Displayers\Label::class,
1055
            'button'      => Displayers\Button::class,
1056
            'link'        => Displayers\Link::class,
1057
            'badge'       => Displayers\Badge::class,
1058
            'progressBar' => Displayers\ProgressBar::class,
1059
            'radio'       => Displayers\Radio::class,
1060
            'checkbox'    => Displayers\Checkbox::class,
1061
            'orderable'   => Displayers\Orderable::class,
1062
            'table'       => Displayers\Table::class,
1063
            'expand'      => Displayers\Expand::class,
1064
        ];
1065
1066
        foreach ($map as $abstract => $class) {
1067
            Column::extend($abstract, $class);
1068
        }
1069
    }
1070
1071
    /**
1072
     * Add variables to grid view.
1073
     *
1074
     * @param array $variables
1075
     *
1076
     * @return $this
1077
     */
1078
    public function with($variables = [])
1079
    {
1080
        $this->variables = $variables;
1081
1082
        return $this;
1083
    }
1084
1085
    /**
1086
     * Get all variables will used in grid view.
1087
     *
1088
     * @return array
1089
     */
1090
    protected function variables()
1091
    {
1092
        $this->variables['grid'] = $this;
1093
1094
        return $this->variables;
1095
    }
1096
1097
    /**
1098
     * Set a view to render.
1099
     *
1100
     * @param string $view
1101
     * @param array  $variables
1102
     */
1103
    public function setView($view, $variables = [])
1104
    {
1105
        if (!empty($variables)) {
1106
            $this->with($variables);
1107
        }
1108
1109
        $this->view = $view;
1110
    }
1111
1112
    /**
1113
     * Set grid title.
1114
     *
1115
     * @param string $title
1116
     *
1117
     * @return $this
1118
     */
1119
    public function setTitle($title)
1120
    {
1121
        $this->variables['title'] = $title;
1122
1123
        return $this;
1124
    }
1125
1126
    /**
1127
     * Set relation for grid.
1128
     *
1129
     * @param Relations\Relation $relation
1130
     *
1131
     * @return $this
1132
     */
1133
    public function setRelation(Relations\Relation $relation)
1134
    {
1135
        $this->model()->setRelation($relation);
1136
1137
        return $this;
1138
    }
1139
1140
    /**
1141
     * Set resource path for grid.
1142
     *
1143
     * @param string $path
1144
     *
1145
     * @return $this
1146
     */
1147
    public function setResource($path)
1148
    {
1149
        $this->resourcePath = $path;
1150
1151
        return $this;
1152
    }
1153
1154
    /**
1155
     * Get the string contents of the grid view.
1156
     *
1157
     * @return string
1158
     */
1159
    public function render()
1160
    {
1161
        $this->handleExportRequest(true);
1162
1163
        try {
1164
            $this->build();
1165
        } catch (\Exception $e) {
1166
            return Handler::renderException($e);
1167
        }
1168
1169
        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...
1170
    }
1171
}
1172