Completed
Pull Request — master (#3054)
by
unknown
02:44
created

Grid::resource()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 14

Duplication

Lines 0
Ratio 0 %

Importance

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