Completed
Pull Request — master (#1350)
by
unknown
03:06
created

Grid   F

Complexity

Total Complexity 98

Size/Duplication

Total Lines 986
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 19

Importance

Changes 0
Metric Value
dl 0
loc 986
rs 1.263
c 0
b 0
f 0
wmc 98
lcom 1
cbo 19

54 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 1
A setupTools() 0 4 1
A setupFilter() 0 4 1
A setupExporter() 0 10 2
A option() 0 10 2
A getKeyName() 0 4 2
B column() 0 23 4
B setLabel() 0 14 6
B columns() 0 18 6
A addColumn() 0 7 1
A model() 0 4 1
A paginate() 0 6 1
A paginator() 0 4 1
A disablePagination() 0 8 1
A usePagination() 0 4 1
A perPages() 0 4 1
A disableActions() 0 4 1
A actions() 0 6 1
A appendActionsColumn() 0 16 2
A disableRowSelector() 0 9 1
A prependRowSelectorColumn() 0 19 2
A build() 0 23 2
A disableFilter() 0 6 1
A getFilter() 0 4 1
A processFilter() 0 6 1
A filter() 0 4 1
A renderFilter() 0 8 2
A buildRows() 0 10 2
A rows() 0 8 2
A tools() 0 4 1
A renderHeaderTools() 0 4 1
A exporter() 0 6 1
A exportUrl() 0 6 1
A allowExport() 0 4 1
A disableExport() 0 4 1
A renderExportButton() 0 4 1
A disableCreation() 0 4 1
A disableCreateButton() 0 4 1
A allowCreation() 0 4 1
A renderCreateButton() 0 4 1
A footer() 0 10 2
A renderFooter() 0 8 2
A resource() 0 14 3
A setDbColumns() 0 6 1
A handleTableColumn() 0 12 3
A handleGetMutatorColumn() 0 8 2
C handleRelationColumn() 0 26 8
B __call() 0 23 6
A registerColumnDisplayer() 0 22 2
A with() 0 6 1
A variables() 0 6 1
A setView() 0 8 2
A render() 0 10 2
A __toString() 0 4 1

How to fix   Complexity   

Complex Class

Complex classes like Grid often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Grid, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Exception\Handler;
7
use Encore\Admin\Grid\Column;
8
use Encore\Admin\Grid\Displayers\Actions;
9
use Encore\Admin\Grid\Displayers\RowSelector;
10
use Encore\Admin\Grid\Exporter;
11
use Encore\Admin\Grid\Filter;
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\BelongsTo;
17
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
18
use Illuminate\Database\Eloquent\Relations\HasMany;
19
use Illuminate\Database\Eloquent\Relations\HasOne;
20
use Illuminate\Database\Eloquent\Relations\MorphToMany;
21
use Illuminate\Database\Eloquent\Relations\Relation;
22
use Illuminate\Support\Collection;
23
use Illuminate\Support\Facades\Input;
24
use Illuminate\Support\Facades\Lang;
25
use Illuminate\Support\Facades\Schema;
26
use Jenssegers\Mongodb\Eloquent\Model as MongodbModel;
27
28
class Grid
29
{
30
    /**
31
     * The grid data model instance.
32
     *
33
     * @var \Encore\Admin\Grid\Model
34
     */
35
    protected $model;
36
37
    /**
38
     * Collection of all grid columns.
39
     *
40
     * @var \Illuminate\Support\Collection
41
     */
42
    protected $columns;
43
44
    /**
45
     * Collection of table columns.
46
     *
47
     * @var \Illuminate\Support\Collection
48
     */
49
    protected $dbColumns;
50
51
    /**
52
     * Collection of all data rows.
53
     *
54
     * @var \Illuminate\Support\Collection
55
     */
56
    protected $rows;
57
58
    /**
59
     * Rows callable fucntion.
60
     *
61
     * @var \Closure
62
     */
63
    protected $rowsCallback;
64
65
    /**
66
     * All column names of the grid.
67
     *
68
     * @var array
69
     */
70
    public $columnNames = [];
71
72
    /**
73
     * Grid builder.
74
     *
75
     * @var \Closure
76
     */
77
    protected $builder;
78
79
    /**
80
     * Mark if the grid is builded.
81
     *
82
     * @var bool
83
     */
84
    protected $builded = false;
85
86
    /**
87
     * All variables in grid view.
88
     *
89
     * @var array
90
     */
91
    protected $variables = [];
92
93
    /**
94
     * The grid Filter.
95
     *
96
     * @var \Encore\Admin\Grid\Filter
97
     */
98
    protected $filter;
99
100
    /**
101
     * Resource path of the grid.
102
     *
103
     * @var
104
     */
105
    protected $resourcePath;
106
107
    /**
108
     * Default primary key name.
109
     *
110
     * @var string
111
     */
112
    protected $keyName = 'id';
113
114
    /**
115
     * Export driver.
116
     *
117
     * @var string
118
     */
119
    protected $exporter;
120
121
    /**
122
     * View for grid to render.
123
     *
124
     * @var string
125
     */
126
    protected $view = 'admin::grid.table';
127
128
    /**
129
     * Per-page options.
130
     *
131
     * @var array
132
     */
133
    public $perPages = [10, 20, 30, 50, 100];
134
135
    /**
136
     * Default items count per-page.
137
     *
138
     * @var int
139
     */
140
    public $perPage = 20;
141
142
    /**
143
     * Header tools.
144
     *
145
     * @var Tools
146
     */
147
    public $tools;
148
149
    /**
150
     * Callback for grid actions.
151
     *
152
     * @var Closure
153
     */
154
    protected $actionsCallback;
155
156
    /**
157
     * Options for grid.
158
     *
159
     * @var array
160
     */
161
    protected $options = [
162
        'usePagination'  => true,
163
        'useFilter'      => true,
164
        'useExporter'    => true,
165
        'useActions'     => true,
166
        'useRowSelector' => true,
167
        'allowCreate'    => true,
168
    ];
169
170
    /**
171
     * @var Tools\Footer
172
     */
173
    protected $footer;
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)
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->setupTools();
190
        $this->setupFilter();
191
        $this->setupExporter();
192
    }
193
194
    /**
195
     * Setup grid tools.
196
     */
197
    public function setupTools()
198
    {
199
        $this->tools = new Tools($this);
200
    }
201
202
    /**
203
     * Setup grid filter.
204
     *
205
     * @return void
206
     */
207
    protected function setupFilter()
208
    {
209
        $this->filter = new Filter($this->model());
210
    }
211
212
    /**
213
     * Setup grid exporter.
214
     *
215
     * @return void
216
     */
217
    protected function setupExporter()
218
    {
219
        if ($scope = Input::get(Exporter::$queryName)) {
220
            $this->model()->usePaginate(false);
221
222
            call_user_func($this->builder, $this);
223
224
            (new Exporter($this))->resolve($this->exporter)->withScope($scope)->export();
225
        }
226
    }
227
228
    /**
229
     * Get or set option for grid.
230
     *
231
     * @param string $key
232
     * @param mixed  $value
233
     *
234
     * @return $this|mixed
235
     */
236
    public function option($key, $value = null)
237
    {
238
        if (is_null($value)) {
239
            return $this->options[$key];
240
        }
241
242
        $this->options[$key] = $value;
243
244
        return $this;
245
    }
246
247
    /**
248
     * Get primary key name of model.
249
     *
250
     * @return string
251
     */
252
    public function getKeyName()
253
    {
254
        return $this->keyName ?: 'id';
255
    }
256
257
    /**
258
     * Add column to Grid.
259
     *
260
     * @param string $name
261
     * @param string $label
262
     *
263
     * @return Column
264
     */
265
    public function column($name, $label = '')
266
    {
267
        $relationName = $relationColumn = '';
268
269
        if (strpos($name, '.') !== false) {
270
            list($relationName, $relationColumn) = explode('.', $name);
271
272
            $relation = $this->model()->eloquent()->$relationName();
273
274
            $label = $this->setLabel($label,$relationColumn);
275
//            $label = empty($label) ? ucfirst($relationColumn) : $label;
0 ignored issues
show
Unused Code Comprehensibility introduced by
58% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
276
            $name = snake_case($relationName).'.'.$relationColumn;
277
        }
278
279
        $column = $this->addColumn($name, $label);
280
281
        if (isset($relation) && $relation instanceof Relation) {
282
            $this->model()->with($relationName);
0 ignored issues
show
Documentation Bug introduced by
The method with does not exist on object<Encore\Admin\Grid\Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
283
            $column->setRelation($relationName, $relationColumn);
284
        }
285
286
        return $column;
287
    }
288
289
    public function setLabel($label , $relationColumn)
290
    {
291
        $trans_key = 'validation.attributes.' . $relationColumn;
292
        $trans_key_low = strtolower($trans_key);
293
         if (empty($label) && Lang::has($trans_key)) {
294
             $label = Lang::get($trans_key);
295
         }
296
         if (empty($label) && Lang::has($trans_key_low)) {
297
            $label = Lang::get($trans_key_low);
298
        }else if (empty($label)) {
299
             $label = ucfirst($relationColumn);
300
         }
301
        return $label;
302
    }
303
304
    /**
305
     * Batch add column to grid.
306
     *
307
     * @example
308
     * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]);
309
     * 2.$grid->columns('name', 'email' ...)
310
     *
311
     * @param array $columns
312
     *
313
     * @return Collection|null
314
     */
315
    public function columns($columns = [])
316
    {
317
        if (func_num_args() == 0) {
318
            return $this->columns;
319
        }
320
321
        if (func_num_args() == 1 && is_array($columns)) {
322
            foreach ($columns as $column => $label) {
323
                $this->column($column, $label);
324
            }
325
326
            return;
327
        }
328
329
        foreach (func_get_args() as $column) {
330
            $this->column($column);
331
        }
332
    }
333
334
    /**
335
     * Add column to grid.
336
     *
337
     * @param string $column
338
     * @param string $label
339
     *
340
     * @return Column
341
     */
342
    protected function addColumn($column = '', $label = '')
343
    {
344
        $column = new Column($column, $label);
345
        $column->setGrid($this);
346
347
        return $this->columns[] = $column;
348
    }
349
350
    /**
351
     * Get Grid model.
352
     *
353
     * @return Model
354
     */
355
    public function model()
356
    {
357
        return $this->model;
358
    }
359
360
    /**
361
     * Paginate the grid.
362
     *
363
     * @param int $perPage
364
     *
365
     * @return void
366
     */
367
    public function paginate($perPage = 20)
368
    {
369
        $this->perPage = $perPage;
370
371
        $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...
372
    }
373
374
    /**
375
     * Get the grid paginator.
376
     *
377
     * @return mixed
378
     */
379
    public function paginator()
380
    {
381
        return new Tools\Paginator($this);
382
    }
383
384
    /**
385
     * Disable grid pagination.
386
     *
387
     * @return $this
388
     */
389
    public function disablePagination()
390
    {
391
        $this->model->usePaginate(false);
392
393
        $this->option('usePagination', false);
394
395
        return $this;
396
    }
397
398
    /**
399
     * If this grid use pagination.
400
     *
401
     * @return bool
402
     */
403
    public function usePagination()
404
    {
405
        return $this->option('usePagination');
406
    }
407
408
    /**
409
     * Set per-page options.
410
     *
411
     * @param array $perPages
412
     */
413
    public function perPages(array $perPages)
414
    {
415
        $this->perPages = $perPages;
416
    }
417
418
    /**
419
     * Disable all actions.
420
     *
421
     * @return $this
422
     */
423
    public function disableActions()
424
    {
425
        return $this->option('useActions', false);
426
    }
427
428
    /**
429
     * Set grid action callback.
430
     *
431
     * @param Closure $callback
432
     *
433
     * @return $this
434
     */
435
    public function actions(Closure $callback)
436
    {
437
        $this->actionsCallback = $callback;
438
439
        return $this;
440
    }
441
442
    /**
443
     * Add `actions` column for grid.
444
     *
445
     * @return void
446
     */
447
    protected function appendActionsColumn()
448
    {
449
        if (!$this->option('useActions')) {
450
            return;
451
        }
452
453
        $grid = $this;
454
        $callback = $this->actionsCallback;
455
        $column = $this->addColumn('__actions__', trans('admin.action'));
456
457
        $column->display(function ($value) use ($grid, $column, $callback) {
458
            $actions = new Actions($value, $grid, $column, $this);
459
460
            return $actions->display($callback);
461
        });
462
    }
463
464
    /**
465
     * Disable row selector.
466
     *
467
     * @return Grid|mixed
468
     */
469
    public function disableRowSelector()
470
    {
471
        $this->tools(function ($tools) {
472
            /* @var Grid\Tools $tools */
473
            $tools->disableBatchActions();
474
        });
475
476
        return $this->option('useRowSelector', false);
477
    }
478
479
    /**
480
     * Prepend checkbox column for grid.
481
     *
482
     * @return void
483
     */
484
    protected function prependRowSelectorColumn()
485
    {
486
        if (!$this->option('useRowSelector')) {
487
            return;
488
        }
489
490
        $grid = $this;
491
492
        $column = new Column(Column::SELECT_COLUMN_NAME, ' ');
493
        $column->setGrid($this);
494
495
        $column->display(function ($value) use ($grid, $column) {
496
            $actions = new RowSelector($value, $grid, $column, $this);
0 ignored issues
show
Documentation introduced by
$this is of type this<Encore\Admin\Grid>, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
497
498
            return $actions->display();
499
        });
500
501
        $this->columns->prepend($column);
502
    }
503
504
    /**
505
     * Build the grid.
506
     *
507
     * @return void
508
     */
509
    public function build()
510
    {
511
        if ($this->builded) {
512
            return;
513
        }
514
515
        $data = $this->processFilter();
516
517
        $this->prependRowSelectorColumn();
518
        $this->appendActionsColumn();
519
520
        Column::setOriginalGridData($data);
521
522
        $this->columns->map(function (Column $column) use (&$data) {
523
            $data = $column->fill($data);
524
525
            $this->columnNames[] = $column->getName();
526
        });
527
528
        $this->buildRows($data);
529
530
        $this->builded = true;
531
    }
532
533
    /**
534
     * Disable grid filter.
535
     *
536
     * @return $this
537
     */
538
    public function disableFilter()
539
    {
540
        $this->option('useFilter', false);
541
542
        return $this;
543
    }
544
545
    /**
546
     * Get filter of Grid.
547
     *
548
     * @return Filter
549
     */
550
    public function getFilter()
551
    {
552
        return $this->filter;
553
    }
554
555
    /**
556
     * Process the grid filter.
557
     *
558
     * @return array
559
     */
560
    public function processFilter()
561
    {
562
        call_user_func($this->builder, $this);
563
564
        return $this->filter->execute();
565
    }
566
567
    /**
568
     * Set the grid filter.
569
     *
570
     * @param Closure $callback
571
     */
572
    public function filter(Closure $callback)
573
    {
574
        call_user_func($callback, $this->filter);
575
    }
576
577
    /**
578
     * Render the grid filter.
579
     *
580
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
581
     */
582
    public function renderFilter()
583
    {
584
        if (!$this->option('useFilter')) {
585
            return '';
586
        }
587
588
        return $this->filter->render();
589
    }
590
591
    /**
592
     * Build the grid rows.
593
     *
594
     * @param array $data
595
     *
596
     * @return void
597
     */
598
    protected function buildRows(array $data)
599
    {
600
        $this->rows = collect($data)->map(function ($model, $number) {
601
            return new Row($number, $model);
602
        });
603
604
        if ($this->rowsCallback) {
605
            $this->rows->map($this->rowsCallback);
606
        }
607
    }
608
609
    /**
610
     * Set grid row callback function.
611
     *
612
     * @param Closure $callable
613
     *
614
     * @return Collection|null
615
     */
616
    public function rows(Closure $callable = null)
617
    {
618
        if (is_null($callable)) {
619
            return $this->rows;
620
        }
621
622
        $this->rowsCallback = $callable;
623
    }
624
625
    /**
626
     * Setup grid tools.
627
     *
628
     * @param Closure $callback
629
     *
630
     * @return void
631
     */
632
    public function tools(Closure $callback)
633
    {
634
        call_user_func($callback, $this->tools);
635
    }
636
637
    /**
638
     * Render custom tools.
639
     *
640
     * @return string
641
     */
642
    public function renderHeaderTools()
643
    {
644
        return $this->tools->render();
645
    }
646
647
    /**
648
     * Set exporter driver for Grid to export.
649
     *
650
     * @param $exporter
651
     *
652
     * @return $this
653
     */
654
    public function exporter($exporter)
655
    {
656
        $this->exporter = $exporter;
657
658
        return $this;
659
    }
660
661
    /**
662
     * Get the export url.
663
     *
664
     * @param int  $scope
665
     * @param null $args
666
     *
667
     * @return string
668
     */
669
    public function exportUrl($scope = 1, $args = null)
670
    {
671
        $input = array_merge(Input::all(), Exporter::formatExportQuery($scope, $args));
672
673
        return $this->resource().'?'.http_build_query($input);
674
    }
675
676
    /**
677
     * If grid allows export.s.
678
     *
679
     * @return bool
680
     */
681
    public function allowExport()
682
    {
683
        return $this->option('useExporter');
684
    }
685
686
    /**
687
     * Disable export.
688
     *
689
     * @return $this
690
     */
691
    public function disableExport()
692
    {
693
        return $this->option('useExporter', false);
694
    }
695
696
    /**
697
     * Render export button.
698
     *
699
     * @return Tools\ExportButton
700
     */
701
    public function renderExportButton()
702
    {
703
        return new Tools\ExportButton($this);
704
    }
705
706
    /**
707
     * Alias for method `disableCreateButton`.
708
     *
709
     * @return $this
710
     *
711
     * @deprecated
712
     */
713
    public function disableCreation()
714
    {
715
        return $this->disableCreateButton();
716
    }
717
718
    /**
719
     * Remove create button on grid.
720
     *
721
     * @return $this
722
     */
723
    public function disableCreateButton()
724
    {
725
        return $this->option('allowCreate', false);
726
    }
727
728
    /**
729
     * If allow creation.
730
     *
731
     * @return bool
732
     */
733
    public function allowCreation()
734
    {
735
        return $this->option('allowCreate');
736
    }
737
738
    /**
739
     * Render create button for grid.
740
     *
741
     * @return Tools\CreateButton
742
     */
743
    public function renderCreateButton()
744
    {
745
        return new Tools\CreateButton($this);
746
    }
747
748
    /**
749
     * Set grid footer.
750
     *
751
     * @param Closure|null $closure
752
     *
753
     * @return $this|Tools\Footer
754
     */
755
    public function footer(Closure $closure = null)
756
    {
757
        if (!$closure) {
758
            return $this->footer;
759
        }
760
761
        $this->footer = $closure;
0 ignored issues
show
Documentation Bug introduced by
It seems like $closure of type object<Closure> is incompatible with the declared type object<Encore\Admin\Grid\Tools\Footer> of property $footer.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
762
763
        return $this;
764
    }
765
766
    /**
767
     * Render grid footer.
768
     *
769
     * @return Tools\Footer|string
770
     */
771
    public function renderFooter()
772
    {
773
        if (!$this->footer) {
774
            return '';
775
        }
776
777
        return new Tools\Footer($this);
778
    }
779
780
    /**
781
     * Get current resource uri.
782
     *
783
     * @param string $path
784
     *
785
     * @return string
786
     */
787
    public function resource($path = null)
788
    {
789
        if (!empty($path)) {
790
            $this->resourcePath = $path;
791
792
            return $this;
793
        }
794
795
        if (!empty($this->resourcePath)) {
796
            return $this->resourcePath;
797
        }
798
799
        return app('request')->getPathInfo();
800
    }
801
802
    /**
803
     * Get the table columns for grid.
804
     *
805
     * @return void
806
     */
807
    protected function setDbColumns()
808
    {
809
        $connection = $this->model()->eloquent()->getConnectionName();
810
811
        $this->dbColumns = collect(Schema::connection($connection)->getColumnListing($this->model()->getTable()));
812
    }
813
814
    /**
815
     * Handle table column for grid.
816
     *
817
     * @param string $method
818
     * @param string $label
819
     *
820
     * @return bool|Column
821
     */
822
    protected function handleTableColumn($method, $label)
823
    {
824
        if (empty($this->dbColumns)) {
825
            $this->setDbColumns();
826
        }
827
828
        if ($this->dbColumns->has($method)) {
829
            return $this->addColumn($method, $label);
830
        }
831
832
        return false;
833
    }
834
835
    /**
836
     * Handle get mutator column for grid.
837
     *
838
     * @param string $method
839
     * @param string $label
840
     *
841
     * @return bool|Column
842
     */
843
    protected function handleGetMutatorColumn($method, $label)
844
    {
845
        if ($this->model()->eloquent()->hasGetMutator($method)) {
846
            return $this->addColumn($method, $label);
847
        }
848
849
        return false;
850
    }
851
852
    /**
853
     * Handle relation column for grid.
854
     *
855
     * @param string $method
856
     * @param string $label
857
     *
858
     * @return bool|Column
859
     */
860
    protected function handleRelationColumn($method, $label)
861
    {
862
        $model = $this->model()->eloquent();
863
864
        if (!method_exists($model, $method)) {
865
            return false;
866
        }
867
868
        if (!($relation = $model->$method()) instanceof Relation) {
869
            return false;
870
        }
871
872
        if ($relation instanceof HasOne || $relation instanceof BelongsTo) {
873
            $this->model()->with($method);
0 ignored issues
show
Documentation Bug introduced by
The method with does not exist on object<Encore\Admin\Grid\Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
874
875
            return $this->addColumn($method, $label)->setRelation(snake_case($method));
876
        }
877
878
        if ($relation instanceof HasMany || $relation instanceof BelongsToMany || $relation instanceof MorphToMany) {
879
            $this->model()->with($method);
0 ignored issues
show
Documentation Bug introduced by
The method with does not exist on object<Encore\Admin\Grid\Model>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
880
881
            return $this->addColumn(snake_case($method), $label);
882
        }
883
884
        return false;
885
    }
886
887
    /**
888
     * Dynamically add columns to the grid view.
889
     *
890
     * @param $method
891
     * @param $arguments
892
     *
893
     * @return Column
894
     */
895
    public function __call($method, $arguments)
896
    {
897
        $label = isset($arguments[0]) ? $arguments[0] : null;
898
        $label = $this->setLabel($label,$method);
899
900
        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...
901
            return $this->addColumn($method, $label);
902
        }
903
904
        if ($column = $this->handleGetMutatorColumn($method, $label)) {
905
            return $column;
906
        }
907
908
        if ($column = $this->handleRelationColumn($method, $label)) {
909
            return $column;
910
        }
911
912
        if ($column = $this->handleTableColumn($method, $label)) {
913
            return $column;
914
        }
915
916
        return $this->addColumn($method, $label);
917
    }
918
919
    /**
920
     * Register column displayers.
921
     *
922
     * @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...
923
     */
924
    public static function registerColumnDisplayer()
925
    {
926
        $map = [
927
            'editable'    => \Encore\Admin\Grid\Displayers\Editable::class,
928
            'switch'      => \Encore\Admin\Grid\Displayers\SwitchDisplay::class,
929
            'switchGroup' => \Encore\Admin\Grid\Displayers\SwitchGroup::class,
930
            'select'      => \Encore\Admin\Grid\Displayers\Select::class,
931
            'image'       => \Encore\Admin\Grid\Displayers\Image::class,
932
            'label'       => \Encore\Admin\Grid\Displayers\Label::class,
933
            'button'      => \Encore\Admin\Grid\Displayers\Button::class,
934
            'link'        => \Encore\Admin\Grid\Displayers\Link::class,
935
            'badge'       => \Encore\Admin\Grid\Displayers\Badge::class,
936
            'progressBar' => \Encore\Admin\Grid\Displayers\ProgressBar::class,
937
            'radio'       => \Encore\Admin\Grid\Displayers\Radio::class,
938
            'checkbox'    => \Encore\Admin\Grid\Displayers\Checkbox::class,
939
            'orderable'   => \Encore\Admin\Grid\Displayers\Orderable::class,
940
        ];
941
942
        foreach ($map as $abstract => $class) {
943
            Column::extend($abstract, $class);
944
        }
945
    }
946
947
    /**
948
     * Add variables to grid view.
949
     *
950
     * @param array $variables
951
     *
952
     * @return $this
953
     */
954
    public function with($variables = [])
955
    {
956
        $this->variables = $variables;
957
958
        return $this;
959
    }
960
961
    /**
962
     * Get all variables will used in grid view.
963
     *
964
     * @return array
965
     */
966
    protected function variables()
967
    {
968
        $this->variables['grid'] = $this;
969
970
        return $this->variables;
971
    }
972
973
    /**
974
     * Set a view to render.
975
     *
976
     * @param string $view
977
     * @param array  $variables
978
     */
979
    public function setView($view, $variables = [])
980
    {
981
        if (!empty($variables)) {
982
            $this->with($variables);
983
        }
984
985
        $this->view = $view;
986
    }
987
988
    /**
989
     * Get the string contents of the grid view.
990
     *
991
     * @return string
992
     */
993
    public function render()
994
    {
995
        try {
996
            $this->build();
997
        } catch (\Exception $e) {
998
            return Handler::renderException($e);
999
        }
1000
1001
        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...
1002
    }
1003
1004
    /**
1005
     * Get the string contents of the grid view.
1006
     *
1007
     * @return string
1008
     */
1009
    public function __toString()
1010
    {
1011
        return $this->render();
1012
    }
1013
}
1014