Completed
Push — master ( 4a68ab...01a40f )
by Song
03:24
created

Column::upload()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Grid;
4
5
use Carbon\Carbon;
6
use Closure;
7
use Encore\Admin\Actions\RowAction;
8
use Encore\Admin\Grid;
9
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
10
use Illuminate\Contracts\Support\Arrayable;
11
use Illuminate\Database\Eloquent\Model as BaseModel;
12
use Illuminate\Support\Arr;
13
use Illuminate\Support\Collection;
14
use Illuminate\Support\Str;
15
16
/**
17
 * Class Column.
18
 *
19
 * @method $this editable()
20
 * @method $this switch ($states = [])
21
 * @method $this switchGroup($columns = [], $states = [])
22
 * @method $this select($options = [])
23
 * @method $this image($server = '', $width = 200, $height = 200)
24
 * @method $this label($style = 'success')
25
 * @method $this button($style = null)
26
 * @method $this link($href = '', $target = '_blank')
27
 * @method $this badge($style = 'red')
28
 * @method $this progress($style = 'primary', $size = 'sm', $max = 100)
29
 * @method $this radio($options = [])
30
 * @method $this checkbox($options = [])
31
 * @method $this orderable($column, $label = '')
32
 * @method $this table($titles = [])
33
 * @method $this expand($callback = null)
34
 * @method $this modal($title, $callback = null)
35
 * @method $this carousel(int $width = 300, int $height = 200, $server = '')
36
 * @method $this downloadable($server = '')
37
 * @method $this copyable()
38
 * @method $this qrcode($formatter = null, $width = 150, $height = 150)
39
 * @method $this prefix($prefix, $delimiter = '&nbsp;')
40
 * @method $this suffix($suffix, $delimiter = '&nbsp;')
41
 * @method $this secret($dotCount = 6)
42
 * @method $this limit($limit = 100, $end = '...')
43
 */
44
class Column
45
{
46
    use Column\HasHeader,
47
        Column\InlineEditing;
48
49
    const SELECT_COLUMN_NAME = '__row_selector__';
50
51
    const ACTION_COLUMN_NAME = '__actions__';
52
53
    /**
54
     * @var Grid
55
     */
56
    protected $grid;
57
58
    /**
59
     * Name of column.
60
     *
61
     * @var string
62
     */
63
    protected $name;
64
65
    /**
66
     * Label of column.
67
     *
68
     * @var string
69
     */
70
    protected $label;
71
72
    /**
73
     * Original value of column.
74
     *
75
     * @var mixed
76
     */
77
    protected $original;
78
79
    /**
80
     * Attributes of column.
81
     *
82
     * @var array
83
     */
84
    protected $attributes = [];
85
86
    /**
87
     * Relation name.
88
     *
89
     * @var bool
90
     */
91
    protected $relation = false;
92
93
    /**
94
     * Relation column.
95
     *
96
     * @var string
97
     */
98
    protected $relationColumn;
99
100
    /**
101
     * Original grid data.
102
     *
103
     * @var Collection
104
     */
105
    protected static $originalGridModels;
106
107
    /**
108
     * @var []Closure
109
     */
110
    protected $displayCallbacks = [];
111
112
    /**
113
     * Displayers for grid column.
114
     *
115
     * @var array
116
     */
117
    public static $displayers = [
118
        'editable'      => Displayers\Editable::class,
119
        'switch'        => Displayers\SwitchDisplay::class,
120
        'switchGroup'   => Displayers\SwitchGroup::class,
121
        'select'        => Displayers\Select::class,
122
        'image'         => Displayers\Image::class,
123
        'label'         => Displayers\Label::class,
124
        'button'        => Displayers\Button::class,
125
        'link'          => Displayers\Link::class,
126
        'badge'         => Displayers\Badge::class,
127
        'progressBar'   => Displayers\ProgressBar::class,
128
        'progress'      => Displayers\ProgressBar::class,
129
        'radio'         => Displayers\Radio::class,
130
        'checkbox'      => Displayers\Checkbox::class,
131
        'orderable'     => Displayers\Orderable::class,
132
        'table'         => Displayers\Table::class,
133
        'expand'        => Displayers\Expand::class,
134
        'modal'         => Displayers\Modal::class,
135
        'carousel'      => Displayers\Carousel::class,
136
        'downloadable'  => Displayers\Downloadable::class,
137
        'copyable'      => Displayers\Copyable::class,
138
        'qrcode'        => Displayers\QRCode::class,
139
        'prefix'        => Displayers\Prefix::class,
140
        'suffix'        => Displayers\Suffix::class,
141
        'secret'        => Displayers\Secret::class,
142
        'limit'         => Displayers\Limit::class,
143
    ];
144
145
    /**
146
     * Defined columns.
147
     *
148
     * @var array
149
     */
150
    public static $defined = [];
151
152
    /**
153
     * @var array
154
     */
155
    protected static $htmlAttributes = [];
156
157
    /**
158
     * @var array
159
     */
160
    protected static $rowAttributes = [];
161
162
    /**
163
     * @var Model
164
     */
165
    protected static $model;
166
167
    /**
168
     * @var bool
169
     */
170
    protected $searchable = false;
171
172
    /**
173
     * @param string $name
174
     * @param string $label
175
     */
176
    public function __construct($name, $label)
177
    {
178
        $this->name = $name;
179
180
        $this->label = $this->formatLabel($label);
181
182
        $this->initAttributes();
183
    }
184
185
    /**
186
     * Initialize column attributes.
187
     */
188
    protected function initAttributes()
189
    {
190
        $name = str_replace('.', '-', $this->name);
191
192
        $this->setAttributes(['class' => "column-{$name}"]);
193
    }
194
195
    /**
196
     * Extend column displayer.
197
     *
198
     * @param $name
199
     * @param $displayer
200
     */
201
    public static function extend($name, $displayer)
202
    {
203
        static::$displayers[$name] = $displayer;
204
    }
205
206
    /**
207
     * Define a column globally.
208
     *
209
     * @param string $name
210
     * @param mixed  $definition
211
     */
212
    public static function define($name, $definition)
213
    {
214
        static::$defined[$name] = $definition;
215
    }
216
217
    /**
218
     * Set grid instance for column.
219
     *
220
     * @param Grid $grid
221
     */
222
    public function setGrid(Grid $grid)
223
    {
224
        $this->grid = $grid;
225
226
        $this->setModel($grid->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...
227
    }
228
229
    /**
230
     * Set model for column.
231
     *
232
     * @param $model
233
     */
234
    public function setModel($model)
235
    {
236
        if (is_null(static::$model) && ($model instanceof BaseModel)) {
237
            static::$model = $model->newInstance();
238
        }
239
    }
240
241
    /**
242
     * Set original data for column.
243
     *
244
     * @param Collection $collection
245
     */
246
    public static function setOriginalGridModels(Collection $collection)
247
    {
248
        static::$originalGridModels = $collection;
249
    }
250
251
    /**
252
     * Set column attributes.
253
     *
254
     * @param array $attributes
255
     *
256
     * @return $this
257
     */
258
    public function setAttributes($attributes = [], $key = null)
259
    {
260
        if ($key) {
261
            static::$rowAttributes[$this->name][$key] = array_merge(
262
                Arr::get(static::$rowAttributes, "{$this->name}.{$key}", []),
263
                $attributes
264
            );
265
266
            return $this;
267
        }
268
269
        static::$htmlAttributes[$this->name] = array_merge(
270
            Arr::get(static::$htmlAttributes, $this->name, []),
271
            $attributes
272
        );
273
274
        return $this;
275
    }
276
277
    /**
278
     * Get column attributes.
279
     *
280
     * @param string $name
281
     *
282
     * @return mixed
283
     */
284
    public static function getAttributes($name, $key = null)
285
    {
286
        $rowAttributes = [];
287
288
        if ($key && Arr::has(static::$rowAttributes, "{$name}.{$key}")) {
289
            $rowAttributes = Arr::get(static::$rowAttributes, "{$name}.{$key}", []);
290
        }
291
292
        $columnAttributes = Arr::get(static::$htmlAttributes, $name, []);
293
294
        return array_merge($rowAttributes, $columnAttributes);
295
    }
296
297
    /**
298
     * Format attributes to html.
299
     *
300
     * @return string
301
     */
302
    public function formatHtmlAttributes()
303
    {
304
        $attrArr = [];
305
        foreach (static::getAttributes($this->name) as $name => $val) {
306
            $attrArr[] = "$name=\"$val\"";
307
        }
308
309
        return implode(' ', $attrArr);
310
    }
311
312
    /**
313
     * Set style of this column.
314
     *
315
     * @param string $style
316
     *
317
     * @return $this
318
     */
319
    public function style($style)
320
    {
321
        return $this->setAttributes(compact('style'));
322
    }
323
324
    /**
325
     * Set the width of column.
326
     *
327
     * @param int $width
328
     *
329
     * @return $this
330
     */
331
    public function width(int $width)
332
    {
333
        return $this->style("width: {$width}px;max-width: {$width}px;word-wrap: break-word;word-break: normal;");
334
    }
335
336
    /**
337
     * Set the color of column.
338
     *
339
     * @param string $color
340
     *
341
     * @return $this
342
     */
343
    public function color($color)
344
    {
345
        return $this->style("color:$color;");
346
    }
347
348
    /**
349
     * Get original column value.
350
     *
351
     * @return mixed
352
     */
353
    public function getOriginal()
354
    {
355
        return $this->original;
356
    }
357
358
    /**
359
     * Get name of this column.
360
     *
361
     * @return mixed
362
     */
363
    public function getName()
364
    {
365
        return $this->name;
366
    }
367
368
    /**
369
     * @return string
370
     */
371
    public function getClassName()
372
    {
373
        $name = str_replace('.', '-', $this->getName());
374
375
        return "column-{$name}";
376
    }
377
378
    /**
379
     * Format label.
380
     *
381
     * @param $label
382
     *
383
     * @return mixed
384
     */
385
    protected function formatLabel($label)
386
    {
387
        if ($label) {
388
            return $label;
389
        }
390
391
        $label = ucfirst($this->name);
392
393
        return __(str_replace(['.', '_'], ' ', $label));
394
    }
395
396
    /**
397
     * Get label of the column.
398
     *
399
     * @return mixed
400
     */
401
    public function getLabel()
402
    {
403
        return $this->label;
404
    }
405
406
    /**
407
     * Set relation.
408
     *
409
     * @param string $relation
410
     * @param string $relationColumn
411
     *
412
     * @return $this
413
     */
414
    public function setRelation($relation, $relationColumn = null)
415
    {
416
        $this->relation = $relation;
0 ignored issues
show
Documentation Bug introduced by
The property $relation was declared of type boolean, but $relation is of type string. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
417
        $this->relationColumn = $relationColumn;
418
419
        return $this;
420
    }
421
422
    /**
423
     * If this column is relation column.
424
     *
425
     * @return bool
426
     */
427
    protected function isRelation()
428
    {
429
        return (bool) $this->relation;
430
    }
431
432
    /**
433
     * Mark this column as sortable.
434
     *
435
     * @param null|string $cast
436
     *
437
     * @return Column|string
438
     */
439
    public function sortable($cast = null)
440
    {
441
        return $this->addSorter($cast);
442
    }
443
444
    /**
445
     * Set cast name for sortable.
446
     *
447
     * @return $this
448
     *
449
     * @deprecated Use `$column->sortable($cast)` instead.
450
     */
451
    public function cast($cast)
452
    {
453
        $this->cast = $cast;
0 ignored issues
show
Bug introduced by
The property cast does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
454
455
        return $this;
456
    }
457
458
    /**
459
     * Set help message for column.
460
     *
461
     * @param string $help
462
     *
463
     * @return $this|string
464
     */
465
    public function help($help = '')
466
    {
467
        return $this->addHelp($help);
468
    }
469
470
    /**
471
     * Set column filter.
472
     *
473
     * @param mixed|null $builder
474
     *
475
     * @return $this
476
     */
477
    public function filter($builder = null)
0 ignored issues
show
Unused Code introduced by
The parameter $builder is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
478
    {
479
        return $this->addFilter(...func_get_args());
480
    }
481
482
    /**
483
     * Set column as searchable.
484
     *
485
     * @return $this
486
     */
487
    public function searchable()
488
    {
489
        $this->searchable = true;
490
491
        $name = $this->getName();
492
        $query = request()->query();
493
494
        $this->prefix(function ($_, $original) use ($name, $query) {
495
            Arr::set($query, $name, $original);
496
497
            $url = request()->fullUrlWithQuery($query);
498
499
            return "<a href=\"{$url}\"><i class=\"fa fa-search\"></i></a>";
500
        }, '&nbsp;&nbsp;');
501
502
        return $this;
503
    }
504
505
    /**
506
     * Bind search query to grid model.
507
     *
508
     * @param Model $model
509
     */
510
    public function bindSearchQuery(Model $model)
511
    {
512
        if ($this->searchable && ($value = request($this->getName())) != '') {
513
            $model->where($this->getName(), $value);
0 ignored issues
show
Documentation Bug introduced by
The method where 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...
514
        }
515
    }
516
517
    /**
518
     * Add a display callback.
519
     *
520
     * @param Closure $callback
521
     *
522
     * @return $this
523
     */
524
    public function display(Closure $callback)
525
    {
526
        $this->displayCallbacks[] = $callback;
527
528
        return $this;
529
    }
530
531
    /**
532
     * Display using display abstract.
533
     *
534
     * @param string $abstract
535
     * @param array  $arguments
536
     *
537
     * @return $this
538
     */
539
    public function displayUsing($abstract, $arguments = [])
540
    {
541
        $grid = $this->grid;
542
543
        $column = $this;
544
545 View Code Duplication
        return $this->display(function ($value) use ($grid, $column, $abstract, $arguments) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
546
            /** @var AbstractDisplayer $displayer */
547
            $displayer = new $abstract($value, $grid, $column, $this);
548
549
            return $displayer->display(...$arguments);
0 ignored issues
show
Unused Code introduced by
The call to AbstractDisplayer::display() has too many arguments starting with $arguments.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
550
        });
551
    }
552
553
    /**
554
     * Display column using array value map.
555
     *
556
     * @param array $values
557
     * @param null  $default
558
     *
559
     * @return $this
560
     */
561 View Code Duplication
    public function using(array $values, $default = null)
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...
562
    {
563
        return $this->display(function ($value) use ($values, $default) {
564
            if (is_null($value)) {
565
                return $default;
566
            }
567
568
            return Arr::get($values, $value, $default);
569
        });
570
    }
571
572
    /**
573
     * Replace output value with giving map.
574
     *
575
     * @param array $replacements
576
     *
577
     * @return $this
578
     */
579
    public function replace(array $replacements)
580
    {
581
        return $this->display(function ($value) use ($replacements) {
582
            if (isset($replacements[$value])) {
583
                return $replacements[$value];
584
            }
585
586
            return $value;
587
        });
588
    }
589
590
    /**
591
     * @param string|Closure $input
592
     * @param string $seperator
593
     *
594
     * @return $this
595
     */
596
    public function repeat($input, $seperator = '')
597
    {
598
        if (is_string($input)) {
599
            $input = function () use ($input) {
600
                return $input;
601
            };
602
        }
603
604
        if ($input instanceof Closure) {
605
            return $this->display(function ($value) use ($input, $seperator) {
606
                return join($seperator, array_fill(0, (int) $value, $input->call($this, [$value])));
607
            });
608
        }
609
610
        return $this;
611
    }
612
613
    /**
614
     * Render this column with the given view.
615
     *
616
     * @param string $view
617
     *
618
     * @return $this
619
     */
620
    public function view($view)
621
    {
622
        return $this->display(function ($value) use ($view) {
623
            $model = $this;
624
625
            return view($view, compact('model', 'value'))->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...
626
        });
627
    }
628
629
    /**
630
     * Hide this column by default.
631
     *
632
     * @return $this
633
     */
634
    public function hide()
635
    {
636
        $this->grid->hideColumns($this->getName());
637
638
        return $this;
639
    }
640
641
    /**
642
     * Add column to total-row.
643
     *
644
     * @param null $display
645
     *
646
     * @return $this
647
     */
648
    public function totalRow($display = null)
649
    {
650
        $this->grid->addTotalRow($this->name, $display);
651
652
        return $this;
653
    }
654
655
    /**
656
     * Convert file size to a human readable format like `100mb`.
657
     *
658
     * @return $this
659
     */
660
    public function filesize()
661
    {
662
        return $this->display(function ($value) {
663
            return file_size($value);
664
        });
665
    }
666
667
    /**
668
     * Display the fields in the email format as gavatar.
669
     *
670
     * @param int $size
671
     *
672
     * @return $this
673
     */
674
    public function gravatar($size = 30)
675
    {
676
        return $this->display(function ($value) use ($size) {
677
            $src = sprintf(
678
                'https://www.gravatar.com/avatar/%s?s=%d',
679
                md5(strtolower($value)),
680
                $size
681
            );
682
683
            return "<img src='$src' class='img img-circle'/>";
684
        });
685
    }
686
687
    /**
688
     * Display field as a loading icon.
689
     *
690
     * @param array $values
691
     * @param array $others
692
     *
693
     * @return $this
694
     */
695
    public function loading($values = [], $others = [])
696
    {
697
        return $this->display(function ($value) use ($values, $others) {
698
            $values = (array) $values;
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $values, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
699
700
            if (in_array($value, $values)) {
701
                return '<i class="fa fa-refresh fa-spin text-primary"></i>';
702
            }
703
704
            return Arr::get($others, $value, $value);
705
        });
706
    }
707
708
    /**
709
     * Display column as an font-awesome icon based on it's value.
710
     *
711
     * @param array  $setting
712
     * @param string $default
713
     *
714
     * @return $this
715
     */
716
    public function icon(array $setting, $default = '')
717
    {
718
        return $this->display(function ($value) use ($setting, $default) {
719
            $fa = '';
720
721
            if (isset($setting[$value])) {
722
                $fa = $setting[$value];
723
            } elseif ($default) {
724
                $fa = $default;
725
            }
726
727
            return "<i class=\"fa fa-{$fa}\"></i>";
728
        });
729
    }
730
731
    /**
732
     * Return a human readable format time.
733
     *
734
     * @param null $locale
735
     *
736
     * @return $this
737
     */
738
    public function diffForHumans($locale = null)
739
    {
740
        if ($locale) {
741
            Carbon::setLocale($locale);
742
        }
743
744
        return $this->display(function ($value) {
745
            return Carbon::parse($value)->diffForHumans();
746
        });
747
    }
748
749
    /**
750
     * Display column as boolean , `✓` for true, and `✗` for false.
751
     *
752
     * @param array $map
753
     * @param bool  $default
754
     *
755
     * @return $this
756
     */
757
    public function bool(array $map = [], $default = false)
758
    {
759
        return $this->display(function ($value) use ($map, $default) {
760
            $bool = empty($map) ? boolval($value) : Arr::get($map, $value, $default);
761
762
            return $bool ? '<i class="fa fa-check text-green"></i>' : '<i class="fa fa-close text-red"></i>';
763
        });
764
    }
765
766
    /**
767
     * Display column as a default value if empty.
768
     *
769
     * @param string $default
770
     * @return $this
771
     */
772
    public function default($default = '-')
773
    {
774
        return $this->display(function ($value) use ($default) {
775
            return $value ?: $default;
776
        });
777
    }
778
779
    /**
780
     * Display column using a grid row action.
781
     *
782
     * @param string $action
783
     *
784
     * @return $this
785
     */
786
    public function action($action)
787
    {
788
        if (!is_subclass_of($action, RowAction::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Encore\Admin\Actions\RowAction::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
789
            throw new \InvalidArgumentException("Action class [$action] must be sub-class of [Encore\Admin\Actions\GridAction]");
790
        }
791
792
        $grid = $this->grid;
793
794
        return $this->display(function ($_, $column) use ($action, $grid) {
795
            /** @var RowAction $action */
796
            $action = new $action();
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $action, or did you forget to import by reference?

It seems like you are assigning to a variable which was imported through a use statement which was not imported by reference.

For clarity, we suggest to use a different name or import by reference depending on whether you would like to have the change visibile in outer-scope.

Change not visible in outer-scope

$x = 1;
$callable = function() use ($x) {
    $x = 2; // Not visible in outer scope. If you would like this, how
            // about using a different variable name than $x?
};

$callable();
var_dump($x); // integer(1)

Change visible in outer-scope

$x = 1;
$callable = function() use (&$x) {
    $x = 2;
};

$callable();
var_dump($x); // integer(2)
Loading history...
797
798
            return $action
799
                ->asColumn()
800
                ->setGrid($grid)
801
                ->setColumn($column)
802
                ->setRow($this);
0 ignored issues
show
Compatibility introduced by
$this of type object<Encore\Admin\Grid\Column> is not a sub-type of object<Illuminate\Database\Eloquent\Model>. It seems like you assume a child class of the class Encore\Admin\Grid\Column to be always present.

This check looks for parameters that are defined as one type in their type hint or doc comment but seem to be used as a narrower type, i.e an implementation of an interface or a subclass.

Consider changing the type of the parameter or doing an instanceof check before assuming your parameter is of the expected type.

Loading history...
803
        });
804
    }
805
806
    /**
807
     * Add a `dot` before column text.
808
     *
809
     * @param array  $options
810
     * @param string $default
811
     *
812
     * @return $this
813
     */
814
    public function dot($options = [], $default = '')
815
    {
816
        return $this->prefix(function ($_, $original) use ($options, $default) {
817
            if (is_null($original)) {
818
                $style = $default;
819
            } else {
820
                $style = Arr::get($options, $original, $default);
821
            }
822
823
            return "<span class=\"label-{$style}\" style='width: 8px;height: 8px;padding: 0;border-radius: 50%;display: inline-block;'></span>";
824
        }, '&nbsp;&nbsp;');
825
    }
826
827
    /**
828
     * If has display callbacks.
829
     *
830
     * @return bool
831
     */
832
    protected function hasDisplayCallbacks()
833
    {
834
        return !empty($this->displayCallbacks);
835
    }
836
837
    /**
838
     * Call all of the "display" callbacks column.
839
     *
840
     * @param mixed $value
841
     * @param int   $key
842
     *
843
     * @return mixed
844
     */
845
    protected function callDisplayCallbacks($value, $key)
846
    {
847
        foreach ($this->displayCallbacks as $callback) {
848
            $previous = $value;
849
850
            $callback = $this->bindOriginalRowModel($callback, $key);
851
            $value = call_user_func_array($callback, [$value, $this]);
852
853
            if (($value instanceof static) &&
854
                ($last = array_pop($this->displayCallbacks))
855
            ) {
856
                $last = $this->bindOriginalRowModel($last, $key);
857
                $value = call_user_func($last, $previous);
858
            }
859
        }
860
861
        return $value;
862
    }
863
864
    /**
865
     * Set original grid data to column.
866
     *
867
     * @param Closure $callback
868
     * @param int     $key
869
     *
870
     * @return Closure
871
     */
872
    protected function bindOriginalRowModel(Closure $callback, $key)
873
    {
874
        $rowModel = static::$originalGridModels[$key];
875
876
        return $callback->bindTo($rowModel);
877
    }
878
879
    /**
880
     * Fill all data to every column.
881
     *
882
     * @param array $data
883
     *
884
     * @return mixed
885
     */
886
    public function fill(array $data)
887
    {
888
        foreach ($data as $key => &$row) {
889
            $this->original = $value = Arr::get($row, $this->name);
890
891
            $value = $this->htmlEntityEncode($value);
892
893
            Arr::set($row, $this->name, $value);
894
895
            if ($this->isDefinedColumn()) {
896
                $this->useDefinedColumn();
897
            }
898
899
            if ($this->hasDisplayCallbacks()) {
900
                $value = $this->callDisplayCallbacks($this->original, $key);
901
                Arr::set($row, $this->name, $value);
902
            }
903
        }
904
905
        return $data;
906
    }
907
908
    /**
909
     * If current column is a defined column.
910
     *
911
     * @return bool
912
     */
913
    protected function isDefinedColumn()
914
    {
915
        return array_key_exists($this->name, static::$defined);
916
    }
917
918
    /**
919
     * Use a defined column.
920
     *
921
     * @throws \Exception
922
     */
923
    protected function useDefinedColumn()
924
    {
925
        // clear all display callbacks.
926
        $this->displayCallbacks = [];
927
928
        $class = static::$defined[$this->name];
929
930
        if ($class instanceof Closure) {
931
            $this->display($class);
932
933
            return;
934
        }
935
936
        if (!class_exists($class) || !is_subclass_of($class, AbstractDisplayer::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Encore\Admin\Grid\Displ...bstractDisplayer::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
937
            throw new \Exception("Invalid column definition [$class]");
938
        }
939
940
        $grid = $this->grid;
941
        $column = $this;
942
943
        $this->display(function ($value) use ($grid, $column, $class) {
944
            /** @var AbstractDisplayer $definition */
945
            $definition = new $class($value, $grid, $column, $this);
946
947
            return $definition->display();
948
        });
949
    }
950
951
    /**
952
     * Convert characters to HTML entities recursively.
953
     *
954
     * @param array|string $item
955
     *
956
     * @return mixed
957
     */
958
    protected function htmlEntityEncode($item)
959
    {
960
        if (is_array($item)) {
961
            array_walk_recursive($item, function (&$value) {
962
                $value = htmlentities($value);
963
            });
964
        } else {
965
            $item = htmlentities($item);
966
        }
967
968
        return $item;
969
    }
970
971
    /**
972
     * Find a displayer to display column.
973
     *
974
     * @param string $abstract
975
     * @param array  $arguments
976
     *
977
     * @return $this
978
     */
979
    protected function resolveDisplayer($abstract, $arguments)
980
    {
981
        if (array_key_exists($abstract, static::$displayers)) {
982
            return $this->callBuiltinDisplayer(static::$displayers[$abstract], $arguments);
983
        }
984
985
        return $this->callSupportDisplayer($abstract, $arguments);
986
    }
987
988
    /**
989
     * Call Illuminate/Support displayer.
990
     *
991
     * @param string $abstract
992
     * @param array  $arguments
993
     *
994
     * @return $this
995
     */
996
    protected function callSupportDisplayer($abstract, $arguments)
997
    {
998
        return $this->display(function ($value) use ($abstract, $arguments) {
999
            if (is_array($value) || $value instanceof Arrayable) {
1000
                return call_user_func_array([collect($value), $abstract], $arguments);
1001
            }
1002
1003
            if (is_string($value)) {
1004
                return call_user_func_array([Str::class, $abstract], array_merge([$value], $arguments));
1005
            }
1006
1007
            return $value;
1008
        });
1009
    }
1010
1011
    /**
1012
     * Call Builtin displayer.
1013
     *
1014
     * @param string $abstract
1015
     * @param array  $arguments
1016
     *
1017
     * @return $this
1018
     */
1019
    protected function callBuiltinDisplayer($abstract, $arguments)
1020
    {
1021
        if ($abstract instanceof Closure) {
1022
            return $this->display(function ($value) use ($abstract, $arguments) {
1023
                return $abstract->call($this, ...array_merge([$value], $arguments));
1024
            });
1025
        }
1026
1027
        if (class_exists($abstract) && is_subclass_of($abstract, AbstractDisplayer::class)) {
0 ignored issues
show
Bug introduced by
Due to PHP Bug #53727, is_subclass_of might return inconsistent results on some PHP versions if \Encore\Admin\Grid\Displ...bstractDisplayer::class can be an interface. If so, you could instead use ReflectionClass::implementsInterface.
Loading history...
1028
            $grid = $this->grid;
1029
            $column = $this;
1030
1031 View Code Duplication
            return $this->display(function ($value) use ($abstract, $grid, $column, $arguments) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
1032
                /** @var AbstractDisplayer $displayer */
1033
                $displayer = new $abstract($value, $grid, $column, $this);
1034
1035
                return $displayer->display(...$arguments);
0 ignored issues
show
Unused Code introduced by
The call to AbstractDisplayer::display() has too many arguments starting with $arguments.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1036
            });
1037
        }
1038
1039
        return $this;
1040
    }
1041
1042
    /**
1043
     * Passes through all unknown calls to builtin displayer or supported displayer.
1044
     *
1045
     * Allow fluent calls on the Column object.
1046
     *
1047
     * @param string $method
1048
     * @param array  $arguments
1049
     *
1050
     * @return $this
1051
     */
1052
    public function __call($method, $arguments)
1053
    {
1054
        if ($this->isRelation() && !$this->relationColumn) {
1055
            $this->name = "{$this->relation}.$method";
1056
            $this->label = $this->formatLabel($arguments[0] ?? null);
1057
1058
            $this->relationColumn = $method;
1059
1060
            return $this;
1061
        }
1062
1063
        return $this->resolveDisplayer($method, $arguments);
1064
    }
1065
}
1066