Completed
Pull Request — master (#4171)
by Muhlis
09:37
created

Grid::columns()   A

Complexity

Conditions 6
Paths 5

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 5
nop 1
dl 0
loc 18
rs 9.0444
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Exception\Handler;
7
use Encore\Admin\Grid\Column;
8
use Encore\Admin\Grid\Concerns;
9
use Encore\Admin\Grid\Displayers;
10
use Encore\Admin\Grid\Exporter;
11
use Encore\Admin\Grid\Exporters\AbstractExporter;
12
use Encore\Admin\Grid\Model;
13
use Encore\Admin\Grid\Row;
14
use Encore\Admin\Grid\Tools;
15
use Illuminate\Database\Eloquent\Model as Eloquent;
16
use Illuminate\Database\Eloquent\Relations;
17
use Illuminate\Support\Collection;
18
use Illuminate\Support\Str;
19
use Illuminate\Support\Traits\Macroable;
20
use Jenssegers\Mongodb\Eloquent\Model as MongodbModel;
21
22
class Grid
23
{
24
    use Concerns\HasElementNames,
25
        Concerns\HasHeader,
26
        Concerns\HasFooter,
27
        Concerns\HasFilter,
28
        Concerns\HasTools,
29
        Concerns\HasTotalRow,
30
        Concerns\HasHotKeys,
31
        Concerns\HasQuickCreate,
32
        Concerns\HasActions,
33
        Concerns\HasSelector,
34
        Concerns\CanHidesColumns,
35
        Concerns\CanFixColumns,
36
        Macroable {
37
            __call as macroCall;
38
        }
39
40
    /**
41
     * The grid data model instance.
42
     *
43
     * @var \Encore\Admin\Grid\Model|\Illuminate\Database\Eloquent\Builder
44
     */
45
    protected $model;
46
47
    /**
48
     * Collection of all grid columns.
49
     *
50
     * @var \Illuminate\Support\Collection
51
     */
52
    protected $columns;
53
54
    /**
55
     * Collection of all data rows.
56
     *
57
     * @var \Illuminate\Support\Collection
58
     */
59
    protected $rows;
60
61
    /**
62
     * Rows callable fucntion.
63
     *
64
     * @var \Closure
65
     */
66
    protected $rowsCallback;
67
68
    /**
69
     * All column names of the grid.
70
     *
71
     * @var array
72
     */
73
    public $columnNames = [];
74
75
    /**
76
     * Grid builder.
77
     *
78
     * @var \Closure
79
     */
80
    protected $builder;
81
82
    /**
83
     * Mark if the grid is builded.
84
     *
85
     * @var bool
86
     */
87
    protected $builded = false;
88
89
    /**
90
     * All variables in grid view.
91
     *
92
     * @var array
93
     */
94
    protected $variables = [];
95
96
    /**
97
     * Resource path of the grid.
98
     *
99
     * @var
100
     */
101
    protected $resourcePath;
102
103
    /**
104
     * Default primary key name.
105
     *
106
     * @var string
107
     */
108
    protected $keyName = 'id';
109
110
    /**
111
     * Export driver.
112
     *
113
     * @var string
114
     */
115
    protected $exporter;
116
117
    /**
118
     * View for grid to render.
119
     *
120
     * @var string
121
     */
122
    protected $view = 'admin::grid.table';
123
124
    /**
125
     * Per-page options.
126
     *
127
     * @var array
128
     */
129
    public $perPages = [10, 20, 30, 50, 100];
130
131
    /**
132
     * Default items count per-page.
133
     *
134
     * @var int
135
     */
136
    public $perPage = 20;
137
138
    /**
139
     * @var []callable
140
     */
141
    protected $renderingCallbacks = [];
142
143
    /**
144
     * Options for grid.
145
     *
146
     * @var array
147
     */
148
    protected $options = [
149
        'show_pagination'        => true,
150
        'show_tools'             => true,
151
        'show_filter'            => true,
152
        'show_exporter'          => true,
153
        'show_actions'           => true,
154
        'show_row_selector'      => true,
155
        'show_create_btn'        => true,
156
        'show_column_selector'   => true,
157
        'show_define_empty_page' => true,
158
    ];
159
160
    /**
161
     * @var string
162
     */
163
    public $tableID;
164
165
    /**
166
     * Initialization closure array.
167
     *
168
     * @var array|\Closure
169
     */
170
    protected static $initCallbacks = [];
171
172
    /**
173
     * Create a new grid instance.
174
     *
175
     * @param Eloquent $model
176
     * @param Closure  $builder
177
     */
178
    public function __construct(Eloquent $model, Closure $builder = null)
179
    {
180
        $this->model = new Model($model, $this);
181
        $this->keyName = $model->getKeyName();
182
        $this->builder = $builder;
183
184
        $this->initialize();
185
186
        $this->handleExportRequest();
187
188
        $this->callInitCallbacks();
189
    }
190
191
    /**
192
     * Initialize.
193
     */
194
    protected function initialize()
195
    {
196
        $this->tableID = uniqid('grid-table');
197
198
        $this->columns = Collection::make();
199
        $this->rows = Collection::make();
200
201
        $this->initTools()
202
            ->initFilter();
203
    }
204
205
    /**
206
     * Initialize with user pre-defined default disables and exporter, etc.
207
     *
208
     * @param Closure $callback
209
     */
210
    public static function init(Closure $callback = null)
211
    {
212
        static::$initCallbacks[] = $callback;
213
    }
214
215
    /**
216
     * Call the initialization closure array in sequence.
217
     */
218
    protected function callInitCallbacks()
219
    {
220
        if (empty(static::$initCallbacks)) {
221
            return;
222
        }
223
224
        foreach (static::$initCallbacks as $callback) {
0 ignored issues
show
Bug introduced by
The expression static::$initCallbacks of type array|object<Closure> is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
225
            call_user_func($callback, $this);
226
        }
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);
0 ignored issues
show
Bug introduced by
The method usePaginate does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
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 a 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
        if (Str::contains($name, '.')) {
308
            return $this->addRelationColumn($name, $label);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->addRelationColumn($name, $label); of type Encore\Admin\Grid|Encore\Admin\Grid\Column adds the type Encore\Admin\Grid to the return on line 308 which is incompatible with the return type documented by Encore\Admin\Grid::column of type Encore\Admin\Grid\Column.
Loading history...
309
        }
310
311
        if (Str::contains($name, '->')) {
312
            return $this->addJsonColumn($name, $label);
313
        }
314
315
        return $this->__call($name, array_filter([$label]));
316
    }
317
318
    /**
319
     * Batch add column to grid.
320
     *
321
     * @example
322
     * 1.$grid->columns(['name' => 'Name', 'email' => 'Email' ...]);
323
     * 2.$grid->columns('name', 'email' ...)
324
     *
325
     * @param array $columns
326
     *
327
     * @return Collection|null
328
     */
329
    public function columns($columns = [])
330
    {
331
        if (func_num_args() == 0) {
332
            return $this->columns;
333
        }
334
335
        if (func_num_args() == 1 && is_array($columns)) {
336
            foreach ($columns as $column => $label) {
337
                $this->column($column, $label);
338
            }
339
340
            return;
341
        }
342
343
        foreach (func_get_args() as $column) {
344
            $this->column($column);
345
        }
346
    }
347
348
    /**
349
     * Add column to grid.
350
     *
351
     * @param string $column
352
     * @param string $label
353
     *
354
     * @return Column
355
     */
356 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...
357
    {
358
        $column = new Column($column, $label);
359
        $column->setGrid($this);
360
361
        return tap($column, function ($value) {
362
            $this->columns->push($value);
363
        });
364
    }
365
366
    /**
367
     * Add a relation column to grid.
368
     *
369
     * @param string $name
370
     * @param string $label
371
     *
372
     * @return $this|bool|Column
373
     */
374
    protected function addRelationColumn($name, $label = '')
375
    {
376
        list($relation, $column) = explode('.', $name);
377
378
        $model = $this->model()->eloquent();
0 ignored issues
show
Bug introduced by
The method eloquent does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
379
380
        if (!method_exists($model, $relation) || !$model->{$relation}() instanceof Relations\Relation) {
381
            $class = get_class($model);
382
383
            admin_error("Call to undefined relationship [{$relation}] on model [{$class}].");
384
385
            return $this;
386
        }
387
388
        $name = Str::snake($relation).'.'.$column;
389
390
        $this->model()->with($relation);
391
392
        return $this->addColumn($name, $label)->setRelation($relation, $column);
393
    }
394
395
    /**
396
     * Add a json type column to grid.
397
     *
398
     * @param string $name
399
     * @param string $label
400
     *
401
     * @return Column
402
     */
403
    protected function addJsonColumn($name, $label = '')
404
    {
405
        $column = substr($name, strrpos($name, '->') + 2);
406
407
        $name = str_replace('->', '.', $name);
408
409
        return $this->addColumn($name, $label ?: ucfirst($column));
410
    }
411
412
    /**
413
     * Prepend column to grid.
414
     *
415
     * @param string $column
416
     * @param string $label
417
     *
418
     * @return Column
419
     */
420 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...
421
    {
422
        $column = new Column($column, $label);
423
        $column->setGrid($this);
424
425
        return tap($column, function ($value) {
426
            $this->columns->prepend($value);
427
        });
428
    }
429
430
    /**
431
     * Get Grid model.
432
     *
433
     * @return Model|\Illuminate\Database\Eloquent\Builder
434
     */
435
    public function model()
436
    {
437
        return $this->model;
438
    }
439
440
    /**
441
     * Paginate the grid.
442
     *
443
     * @param int $perPage
444
     *
445
     * @return void
446
     */
447
    public function paginate($perPage = 20)
448
    {
449
        $this->perPage = $perPage;
450
451
        $this->model()->setPerPage($perPage);
0 ignored issues
show
Bug introduced by
The method setPerPage does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
452
    }
453
454
    /**
455
     * Get the grid paginator.
456
     *
457
     * @return mixed
458
     */
459
    public function paginator()
460
    {
461
        return new Tools\Paginator($this);
462
    }
463
464
    /**
465
     * Disable grid pagination.
466
     *
467
     * @return $this
468
     */
469
    public function disablePagination(bool $disable = true)
470
    {
471
        $this->model->usePaginate(!$disable);
0 ignored issues
show
Bug introduced by
The method usePaginate does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
472
473
        return $this->option('show_pagination', !$disable);
474
    }
475
476
    /**
477
     * If this grid use pagination.
478
     *
479
     * @return bool
480
     */
481
    public function showPagination()
482
    {
483
        return $this->option('show_pagination');
484
    }
485
486
    /**
487
     * Set per-page options.
488
     *
489
     * @param array $perPages
490
     */
491
    public function perPages(array $perPages)
492
    {
493
        $this->perPages = $perPages;
494
    }
495
496
    /**
497
     * Disable row selector.
498
     *
499
     * @return Grid|mixed
500
     */
501
    public function disableRowSelector(bool $disable = true)
502
    {
503
        return $this->disableBatchActions($disable);
504
    }
505
506
    /**
507
     * Prepend checkbox column for grid.
508
     *
509
     * @return void
510
     */
511
    protected function prependRowSelectorColumn()
512
    {
513
        if (!$this->option('show_row_selector')) {
514
            return;
515
        }
516
517
        $checkAllBox = "<input type=\"checkbox\" class=\"{$this->getSelectAllName()}\" />&nbsp;";
518
519
        $this->prependColumn(Column::SELECT_COLUMN_NAME, ' ')
520
            ->displayUsing(Displayers\RowSelector::class)
521
            ->addHeader($checkAllBox);
522
    }
523
524
    /**
525
     * Apply column filter to grid query.
526
     *
527
     * @return void
528
     */
529
    protected function applyColumnFilter()
530
    {
531
        $this->columns->each->bindFilterQuery($this->model());
532
    }
533
534
    /**
535
     * Apply column search to grid query.
536
     *
537
     * @return void
538
     */
539
    protected function applyColumnSearch()
540
    {
541
        $this->columns->each->bindSearchQuery($this->model());
542
    }
543
544
    /**
545
     * @return array|Collection|mixed
546
     */
547
    protected function applyQuery()
548
    {
549
        $this->applyQuickSearch();
550
551
        $this->applyColumnFilter();
552
553
        $this->applyColumnSearch();
554
555
        $this->applySelectorQuery();
556
557
        return $this->applyFilter(false);
558
    }
559
560
    /**
561
     * Add row selector columns and action columns before and after the grid.
562
     *
563
     * @return void
564
     */
565
    protected function addDefaultColumns()
566
    {
567
        $this->prependRowSelectorColumn();
568
569
        $this->appendActionsColumn();
570
    }
571
572
    /**
573
     * Build the grid.
574
     *
575
     * @return void
576
     */
577
    public function build()
578
    {
579
        if ($this->builded) {
580
            return;
581
        }
582
583
        $collection = $this->applyQuery();
584
585
        $this->addDefaultColumns();
586
587
        Column::setOriginalGridModels($collection);
588
589
        $data = $collection->toArray();
590
591
        $this->columns->map(function (Column $column) use (&$data) {
592
            $data = $column->fill($data);
593
594
            $this->columnNames[] = $column->getName();
595
        });
596
597
        $this->buildRows($data);
598
599
        $this->builded = true;
600
    }
601
602
    /**
603
     * Build the grid rows.
604
     *
605
     * @param array $data
606
     *
607
     * @return void
608
     */
609
    protected function buildRows(array $data)
610
    {
611
        $this->rows = collect($data)->map(function ($model, $number) {
612
            return new Row($number, $model, $this->keyName);
613
        });
614
615
        if ($this->rowsCallback) {
616
            $this->rows->map($this->rowsCallback);
617
        }
618
    }
619
620
    /**
621
     * Set grid row callback function.
622
     *
623
     * @param Closure $callable
624
     *
625
     * @return Collection|null
626
     */
627
    public function rows(Closure $callable = null)
628
    {
629
        if (is_null($callable)) {
630
            return $this->rows;
631
        }
632
633
        $this->rowsCallback = $callable;
634
    }
635
636
    /**
637
     * Set exporter driver for Grid to export.
638
     *
639
     * @param $exporter
640
     *
641
     * @return $this
642
     */
643
    public function exporter($exporter)
644
    {
645
        $this->exporter = $exporter;
646
647
        return $this;
648
    }
649
650
    /**
651
     * Get the export url.
652
     *
653
     * @param int $scope
654
     * @param $args
655
     *
656
     * @return string
657
     */
658
    public function getExportUrl($scope = 1, $args = null)
659
    {
660
        $input = array_merge(request()->all(), Exporter::formatExportQuery($scope, $args));
661
662
        if ($constraints = $this->model()->getConstraints()) {
0 ignored issues
show
Bug introduced by
The method getConstraints does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
663
            $input = array_merge($input, $constraints);
664
        }
665
666
        return $this->resource().'?'.http_build_query($input);
667
    }
668
669
    /**
670
     * Get create url.
671
     *
672
     * @return string
673
     */
674
    public function getCreateUrl()
675
    {
676
        $queryString = '';
677
678
        if ($constraints = $this->model()->getConstraints()) {
0 ignored issues
show
Bug introduced by
The method getConstraints does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
679
            $queryString = http_build_query($constraints);
680
        }
681
682
        return sprintf('%s/create%s',
683
            $this->resource(),
684
            $queryString ? ('?'.$queryString) : ''
685
        );
686
    }
687
688
    /**
689
     * If grid show export btn.
690
     *
691
     * @return bool
692
     */
693
    public function showExportBtn()
694
    {
695
        return $this->option('show_exporter');
696
    }
697
698
    /**
699
     * Disable export.
700
     *
701
     * @return $this
702
     */
703
    public function disableExport(bool $disable = true)
704
    {
705
        return $this->option('show_exporter', !$disable);
706
    }
707
708
    /**
709
     * Render export button.
710
     *
711
     * @return string
712
     */
713
    public function renderExportButton()
714
    {
715
        return (new Tools\ExportButton($this))->render();
716
    }
717
718
    /**
719
     * Alias for method `disableCreateButton`.
720
     *
721
     * @return $this
722
     *
723
     * @deprecated
724
     */
725
    public function disableCreation()
726
    {
727
        return $this->disableCreateButton();
728
    }
729
730
    /**
731
     * Remove create button on grid.
732
     *
733
     * @return $this
734
     */
735
    public function disableCreateButton(bool $disable = true)
736
    {
737
        return $this->option('show_create_btn', !$disable);
738
    }
739
740
    /**
741
     * Remove define empty page on grid.
742
     *
743
     * @return $this
744
     */
745
    public function disableDefineEmptyPage(bool $disable = true)
746
    {
747
        return $this->option('show_define_empty_page', !$disable);
748
    }
749
750
    /**
751
     * If grid show define empty page on grid.
752
     *
753
     * @return bool
754
     */
755
    public function showDefineEmptyPage()
756
    {
757
        return $this->option('show_define_empty_page');
758
    }
759
760
    /**
761
     * If allow creation.
762
     *
763
     * @return bool
764
     */
765
    public function showCreateBtn()
766
    {
767
        return $this->option('show_create_btn');
768
    }
769
770
    /**
771
     * Render create button for grid.
772
     *
773
     * @return string
774
     */
775
    public function renderCreateButton()
776
    {
777
        return (new Tools\CreateButton($this))->render();
778
    }
779
780
    /**
781
     * Get current resource url.
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;
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...
793
        }
794
795
        if (!empty($this->resourcePath)) {
796
            return $this->resourcePath;
797
        }
798
799
        return url(app('request')->getPathInfo());
0 ignored issues
show
Bug Compatibility introduced by
The expression url(app('request')->getPathInfo()); of type Illuminate\Contracts\Routing\UrlGenerator|string adds the type Illuminate\Contracts\Routing\UrlGenerator to the return on line 799 which is incompatible with the return type documented by Encore\Admin\Grid::resource of type string.
Loading history...
800
    }
801
802
    /**
803
     * Handle get mutator column for grid.
804
     *
805
     * @param string $method
806
     * @param string $label
807
     *
808
     * @return bool|Column
809
     */
810
    protected function handleGetMutatorColumn($method, $label)
811
    {
812
        if ($this->model()->eloquent()->hasGetMutator($method)) {
0 ignored issues
show
Bug introduced by
The method eloquent does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
813
            return $this->addColumn($method, $label);
814
        }
815
816
        return false;
817
    }
818
819
    /**
820
     * Handle relation column for grid.
821
     *
822
     * @param string $method
823
     * @param string $label
824
     *
825
     * @return bool|Column
826
     */
827
    protected function handleRelationColumn($method, $label)
828
    {
829
        $model = $this->model()->eloquent();
0 ignored issues
show
Bug introduced by
The method eloquent does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
830
831
        if (!method_exists($model, $method)) {
832
            return false;
833
        }
834
835
        if (!($relation = $model->$method()) instanceof Relations\Relation) {
836
            return false;
837
        }
838
839
        if ($relation instanceof Relations\HasOne
840
            || $relation instanceof Relations\BelongsTo
841
            || $relation instanceof Relations\MorphOne
842
            || $relation instanceof Relations\HasOneThrough
843
            || $relation instanceof \Znck\Eloquent\Relations\belongsToThrough
844
            || $relation instanceof \Staudenmeir\EloquentHasManyDeep\HasOneDeep
845
        ) {
846
            $this->model()->with($method);
847
848
            return $this->addColumn($method, $label)->setRelation(Str::snake($method));
849
        }
850
851
        if ($relation instanceof Relations\HasMany
852
            || $relation instanceof Relations\BelongsToMany
853
            || $relation instanceof Relations\MorphToMany
854
            || $relation instanceof Relations\HasManyThrough
855
        ) {
856
            $this->model()->with($method);
857
858
            return $this->addColumn(Str::snake($method), $label);
859
        }
860
861
        return false;
862
    }
863
864
    /**
865
     * Dynamically add columns to the grid view.
866
     *
867
     * @param $method
868
     * @param $arguments
869
     *
870
     * @return Column
871
     */
872
    public function __call($method, $arguments)
873
    {
874
        if (static::hasMacro($method)) {
875
            return $this->macroCall($method, $arguments);
876
        }
877
878
        $label = $arguments[0] ?? null;
879
880
        if ($this->model()->eloquent() instanceof MongodbModel) {
0 ignored issues
show
Bug introduced by
The method eloquent does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
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...
881
            return $this->addColumn($method, $label);
882
        }
883
884
        if ($column = $this->handleGetMutatorColumn($method, $label)) {
885
            return $column;
886
        }
887
888
        if ($column = $this->handleRelationColumn($method, $label)) {
889
            return $column;
890
        }
891
892
        return $this->addColumn($method, $label);
893
    }
894
895
    /**
896
     * Add variables to grid view.
897
     *
898
     * @param array $variables
899
     *
900
     * @return $this
901
     */
902
    public function with($variables = [])
903
    {
904
        $this->variables = $variables;
905
906
        return $this;
907
    }
908
909
    /**
910
     * Get all variables will used in grid view.
911
     *
912
     * @return array
913
     */
914
    protected function variables()
915
    {
916
        $this->variables['grid'] = $this;
917
918
        return $this->variables;
919
    }
920
921
    /**
922
     * Set a view to render.
923
     *
924
     * @param string $view
925
     * @param array  $variables
926
     */
927
    public function setView($view, $variables = [])
928
    {
929
        if (!empty($variables)) {
930
            $this->with($variables);
931
        }
932
933
        $this->view = $view;
934
    }
935
936
    /**
937
     * Set grid title.
938
     *
939
     * @param string $title
940
     *
941
     * @return $this
942
     */
943
    public function setTitle($title)
944
    {
945
        $this->variables['title'] = $title;
946
947
        return $this;
948
    }
949
950
    /**
951
     * Set relation for grid.
952
     *
953
     * @param Relations\Relation $relation
954
     *
955
     * @return $this
956
     */
957
    public function setRelation(Relations\Relation $relation)
958
    {
959
        $this->model()->setRelation($relation);
0 ignored issues
show
Bug introduced by
The method setRelation does only exist in Encore\Admin\Grid\Model, but not in Illuminate\Database\Eloquent\Builder.

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...
960
961
        return $this;
962
    }
963
964
    /**
965
     * Set resource path for grid.
966
     *
967
     * @param string $path
968
     *
969
     * @return $this
970
     */
971
    public function setResource($path)
972
    {
973
        $this->resourcePath = $path;
974
975
        return $this;
976
    }
977
978
    /**
979
     * Set rendering callback.
980
     *
981
     * @param callable $callback
982
     *
983
     * @return $this
984
     */
985
    public function rendering(callable $callback)
986
    {
987
        $this->renderingCallbacks[] = $callback;
988
989
        return $this;
990
    }
991
992
    /**
993
     * Call callbacks before render.
994
     *
995
     * @return void
996
     */
997
    protected function callRenderingCallback()
998
    {
999
        foreach ($this->renderingCallbacks as $callback) {
1000
            call_user_func($callback, $this);
1001
        }
1002
    }
1003
1004
    /**
1005
     * Get the string contents of the grid view.
1006
     *
1007
     * @return string
1008
     */
1009
    public function render()
1010
    {
1011
        $this->handleExportRequest(true);
1012
1013
        try {
1014
            $this->build();
1015
        } catch (\Exception $e) {
1016
            return Handler::renderException($e);
1017
        }
1018
1019
        $this->callRenderingCallback();
1020
1021
        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...
1022
    }
1023
}
1024