Completed
Push — master ( ee487f...06f79e )
by Song
02:44
created

Column::dot()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 1
nop 2
dl 0
loc 12
rs 9.8666
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($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
 */
42
class Column
43
{
44
    use Column\HasHeader;
45
46
    const SELECT_COLUMN_NAME = '__row_selector__';
47
48
    const ACTION_COLUMN_NAME = '__actions__';
49
50
    /**
51
     * @var Grid
52
     */
53
    protected $grid;
54
55
    /**
56
     * Name of column.
57
     *
58
     * @var string
59
     */
60
    protected $name;
61
62
    /**
63
     * Label of column.
64
     *
65
     * @var string
66
     */
67
    protected $label;
68
69
    /**
70
     * Original value of column.
71
     *
72
     * @var mixed
73
     */
74
    protected $original;
75
76
    /**
77
     * Attributes of column.
78
     *
79
     * @var array
80
     */
81
    protected $attributes = [];
82
83
    /**
84
     * Relation name.
85
     *
86
     * @var bool
87
     */
88
    protected $relation = false;
89
90
    /**
91
     * Relation column.
92
     *
93
     * @var string
94
     */
95
    protected $relationColumn;
96
97
    /**
98
     * Original grid data.
99
     *
100
     * @var Collection
101
     */
102
    protected static $originalGridModels;
103
104
    /**
105
     * @var []Closure
106
     */
107
    protected $displayCallbacks = [];
108
109
    /**
110
     * Displayers for grid column.
111
     *
112
     * @var array
113
     */
114
    public static $displayers = [
115
        'editable'    => Displayers\Editable::class,
116
        'switch'      => Displayers\SwitchDisplay::class,
117
        'switchGroup' => Displayers\SwitchGroup::class,
118
        'select'      => Displayers\Select::class,
119
        'image'       => Displayers\Image::class,
120
        'label'       => Displayers\Label::class,
121
        'button'      => Displayers\Button::class,
122
        'link'        => Displayers\Link::class,
123
        'badge'       => Displayers\Badge::class,
124
        'progressBar' => Displayers\ProgressBar::class,
125
        'progress'    => Displayers\ProgressBar::class,
126
        'radio'       => Displayers\Radio::class,
127
        'checkbox'    => Displayers\Checkbox::class,
128
        'orderable'   => Displayers\Orderable::class,
129
        'table'       => Displayers\Table::class,
130
        'expand'      => Displayers\Expand::class,
131
        'modal'       => Displayers\Modal::class,
132
        'carousel'    => Displayers\Carousel::class,
133
        'downloadable'=> Displayers\Downloadable::class,
134
        'copyable'    => Displayers\Copyable::class,
135
        'qrcode'      => Displayers\QRCode::class,
136
        'prefix'      => Displayers\Prefix::class,
137
        'suffix'      => Displayers\Suffix::class,
138
    ];
139
140
    /**
141
     * Defined columns.
142
     *
143
     * @var array
144
     */
145
    public static $defined = [];
146
147
    /**
148
     * @var array
149
     */
150
    protected static $htmlAttributes = [];
151
152
    /**
153
     * @var array
154
     */
155
    protected static $rowAttributes = [];
156
157
    /**
158
     * @var Model
159
     */
160
    protected static $model;
161
162
    /**
163
     * @param string $name
164
     * @param string $label
165
     */
166
    public function __construct($name, $label)
167
    {
168
        $this->name = $name;
169
170
        $this->label = $this->formatLabel($label);
171
172
        $this->initAttributes();
173
    }
174
175
    /**
176
     * Initialize column attributes.
177
     */
178
    protected function initAttributes()
179
    {
180
        $name = str_replace('.', '-', $this->name);
181
182
        $this->setAttributes(['class' => "column-{$name}"]);
183
    }
184
185
    /**
186
     * Extend column displayer.
187
     *
188
     * @param $name
189
     * @param $displayer
190
     */
191
    public static function extend($name, $displayer)
192
    {
193
        static::$displayers[$name] = $displayer;
194
    }
195
196
    /**
197
     * Define a column globally.
198
     *
199
     * @param string $name
200
     * @param mixed  $definition
201
     */
202
    public static function define($name, $definition)
203
    {
204
        static::$defined[$name] = $definition;
205
    }
206
207
    /**
208
     * Set grid instance for column.
209
     *
210
     * @param Grid $grid
211
     */
212
    public function setGrid(Grid $grid)
213
    {
214
        $this->grid = $grid;
215
216
        $this->setModel($grid->model()->eloquent());
217
    }
218
219
    /**
220
     * Set model for column.
221
     *
222
     * @param $model
223
     */
224
    public function setModel($model)
225
    {
226
        if (is_null(static::$model) && ($model instanceof BaseModel)) {
227
            static::$model = $model->newInstance();
228
        }
229
    }
230
231
    /**
232
     * Set original data for column.
233
     *
234
     * @param Collection $collection
235
     */
236
    public static function setOriginalGridModels(Collection $collection)
237
    {
238
        static::$originalGridModels = $collection;
239
    }
240
241
    /**
242
     * Set column attributes.
243
     *
244
     * @param array $attributes
245
     *
246
     * @return $this
247
     */
248
    public function setAttributes($attributes = [], $key = null)
249
    {
250
        if ($key) {
251
            static::$rowAttributes[$this->name][$key] = array_merge(
252
                Arr::get(static::$rowAttributes, "{$this->name}.{$key}", []),
253
                $attributes
254
            );
255
256
            return $this;
257
        }
258
259
        static::$htmlAttributes[$this->name] = array_merge(
260
            Arr::get(static::$htmlAttributes, $this->name, []),
261
            $attributes
262
        );
263
264
        return $this;
265
    }
266
267
    /**
268
     * Get column attributes.
269
     *
270
     * @param string $name
271
     *
272
     * @return mixed
273
     */
274
    public static function getAttributes($name, $key = null)
275
    {
276
        $rowAttributes = [];
277
278
        if ($key && Arr::has(static::$rowAttributes, "{$name}.{$key}")) {
279
            $rowAttributes = Arr::get(static::$rowAttributes, "{$name}.{$key}", []);
280
        }
281
282
        $columnAttributes = Arr::get(static::$htmlAttributes, $name, []);
283
284
        return array_merge($rowAttributes, $columnAttributes);
285
    }
286
287
    /**
288
     * Format attributes to html.
289
     *
290
     * @return string
291
     */
292
    public function formatHtmlAttributes()
293
    {
294
        $attrArr = [];
295
        foreach (static::getAttributes($this->name) as $name => $val) {
296
            $attrArr[] = "$name=\"$val\"";
297
        }
298
299
        return implode(' ', $attrArr);
300
    }
301
302
    /**
303
     * Set style of this column.
304
     *
305
     * @param string $style
306
     *
307
     * @return $this
308
     */
309
    public function style($style)
310
    {
311
        return $this->setAttributes(compact('style'));
312
    }
313
314
    /**
315
     * Set the width of column.
316
     *
317
     * @param int $width
318
     *
319
     * @return $this
320
     */
321
    public function width(int $width)
322
    {
323
        return $this->style("width: {$width}px;");
324
    }
325
326
    /**
327
     * Set the color of column.
328
     *
329
     * @param string $color
330
     *
331
     * @return $this
332
     */
333
    public function color($color)
334
    {
335
        return $this->style("color:$color;");
336
    }
337
338
    /**
339
     * Get original column value.
340
     *
341
     * @return mixed
342
     */
343
    public function getOriginal()
344
    {
345
        return $this->original;
346
    }
347
348
    /**
349
     * Get name of this column.
350
     *
351
     * @return mixed
352
     */
353
    public function getName()
354
    {
355
        return $this->name;
356
    }
357
358
    /**
359
     * @return string
360
     */
361
    public function getClassName()
362
    {
363
        $name = str_replace('.', '-', $this->getName());
364
365
        return "column-{$name}";
366
    }
367
368
    /**
369
     * Format label.
370
     *
371
     * @param $label
372
     *
373
     * @return mixed
374
     */
375
    protected function formatLabel($label)
376
    {
377
        if ($label) {
378
            return $label;
379
        }
380
381
        $label = ucfirst($this->name);
382
383
        return __(str_replace(['.', '_'], ' ', $label));
384
    }
385
386
    /**
387
     * Get label of the column.
388
     *
389
     * @return mixed
390
     */
391
    public function getLabel()
392
    {
393
        return $this->label;
394
    }
395
396
    /**
397
     * Set relation.
398
     *
399
     * @param string $relation
400
     * @param string $relationColumn
401
     *
402
     * @return $this
403
     */
404
    public function setRelation($relation, $relationColumn = null)
405
    {
406
        $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...
407
        $this->relationColumn = $relationColumn;
408
409
        return $this;
410
    }
411
412
    /**
413
     * If this column is relation column.
414
     *
415
     * @return bool
416
     */
417
    protected function isRelation()
418
    {
419
        return (bool) $this->relation;
420
    }
421
422
    /**
423
     * Mark this column as sortable.
424
     *
425
     * @param null|string $cast
426
     *
427
     * @return Column|string
428
     */
429
    public function sortable($cast = null)
430
    {
431
        return $this->addSorter($cast);
432
    }
433
434
    /**
435
     * Set cast name for sortable.
436
     *
437
     * @return $this
438
     *
439
     * @deprecated Use `$column->sortable($cast)` instead.
440
     */
441
    public function cast($cast)
442
    {
443
        $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...
444
445
        return $this;
446
    }
447
448
    /**
449
     * Set help message for column.
450
     *
451
     * @param string $help
452
     *
453
     * @return $this|string
454
     */
455
    public function help($help = '')
456
    {
457
        return $this->addHelp($help);
458
    }
459
460
    /**
461
     * Set column filter.
462
     *
463
     * @param null $builder
464
     *
465
     * @return $this
466
     */
467
    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...
468
    {
469
        return $this->addFilter(...func_get_args());
470
    }
471
472
    /**
473
     * Add a display callback.
474
     *
475
     * @param Closure $callback
476
     *
477
     * @return $this
478
     */
479
    public function display(Closure $callback)
480
    {
481
        $this->displayCallbacks[] = $callback;
482
483
        return $this;
484
    }
485
486
    /**
487
     * Display using display abstract.
488
     *
489
     * @param string $abstract
490
     * @param array  $arguments
491
     *
492
     * @return $this
493
     */
494
    public function displayUsing($abstract, $arguments = [])
495
    {
496
        $grid = $this->grid;
497
498
        $column = $this;
499
500 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...
501
            /** @var AbstractDisplayer $displayer */
502
            $displayer = new $abstract($value, $grid, $column, $this);
503
504
            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...
505
        });
506
    }
507
508
    /**
509
     * Display column using array value map.
510
     *
511
     * @param array $values
512
     * @param null  $default
513
     *
514
     * @return $this
515
     */
516 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...
517
    {
518
        return $this->display(function ($value) use ($values, $default) {
519
            if (is_null($value)) {
520
                return $default;
521
            }
522
523
            return Arr::get($values, $value, $default);
524
        });
525
    }
526
527
    /**
528
     * Replace output value with giving map.
529
     *
530
     * @param array $replacements
531
     *
532
     * @return $this
533
     */
534
    public function replace(array $replacements)
535
    {
536
        return $this->display(function ($value) use ($replacements) {
537
            if (isset($replacements[$value])) {
538
                return $replacements[$value];
539
            }
540
541
            return $value;
542
        });
543
    }
544
545
    /**
546
     * Render this column with the given view.
547
     *
548
     * @param string $view
549
     *
550
     * @return $this
551
     */
552
    public function view($view)
553
    {
554
        return $this->display(function ($value) use ($view) {
555
            $model = $this;
556
557
            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...
558
        });
559
    }
560
561
    /**
562
     * Hide this column by default.
563
     *
564
     * @return $this
565
     */
566
    public function hide()
567
    {
568
        $this->grid->hideColumns($this->getName());
569
570
        return $this;
571
    }
572
573
    /**
574
     * Add column to total-row.
575
     *
576
     * @param null $display
577
     *
578
     * @return $this
579
     */
580
    public function totalRow($display = null)
581
    {
582
        $this->grid->addTotalRow($this->name, $display);
0 ignored issues
show
Documentation introduced by
$display is of type null, but the function expects a object<Closure>.

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...
583
584
        return $this;
585
    }
586
587
    /**
588
     * Convert file size to a human readable format like `100mb`.
589
     *
590
     * @return $this
591
     */
592
    public function filesize()
593
    {
594
        return $this->display(function ($value) {
595
            return file_size($value);
596
        });
597
    }
598
599
    /**
600
     * Display the fields in the email format as gavatar.
601
     *
602
     * @param int $size
603
     *
604
     * @return $this
605
     */
606
    public function gravatar($size = 30)
607
    {
608
        return $this->display(function ($value) use ($size) {
609
            $src = sprintf(
610
                'https://www.gravatar.com/avatar/%s?s=%d',
611
                md5(strtolower($value)),
612
                $size
613
            );
614
615
            return "<img src='$src' class='img img-circle'/>";
616
        });
617
    }
618
619
    /**
620
     * Display field as a loading icon.
621
     *
622
     * @param array $values
623
     * @param array $others
624
     *
625
     * @return $this
626
     */
627
    public function loading($values = [], $others = [])
628
    {
629
        return $this->display(function ($value) use ($values, $others) {
630
            $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...
631
632
            if (in_array($value, $values)) {
633
                return '<i class="fa fa-refresh fa-spin text-primary"></i>';
634
            }
635
636
            return Arr::get($others, $value, $value);
637
        });
638
    }
639
640
    /**
641
     * Display column as an font-awesome icon based on it's value.
642
     *
643
     * @param array  $setting
644
     * @param string $default
645
     *
646
     * @return $this
647
     */
648
    public function icon(array $setting, $default = '')
649
    {
650
        return $this->display(function ($value) use ($setting, $default) {
651
            $fa = '';
652
653
            if (isset($setting[$value])) {
654
                $fa = $setting[$value];
655
            } elseif ($default) {
656
                $fa = $default;
657
            }
658
659
            return "<i class=\"fa fa-{$fa}\"></i>";
660
        });
661
    }
662
663
    /**
664
     * Return a human readable format time.
665
     *
666
     * @param null $locale
667
     *
668
     * @return $this
669
     */
670
    public function diffForHumans($locale = null)
671
    {
672
        if ($locale) {
673
            Carbon::setLocale($locale);
674
        }
675
676
        return $this->display(function ($value) {
677
            return Carbon::parse($value)->diffForHumans();
0 ignored issues
show
Bug introduced by
The method diffForHumans does only exist in Carbon\CarbonInterface, but not in Carbon\Traits\Creator.

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...
678
        });
679
    }
680
681
    /**
682
     * Returns a string formatted according to the given format string.
683
     *
684
     * @param string $format
685
     *
686
     * @return $this
687
     */
688
    public function date($format)
689
    {
690
        return $this->display(function ($value) use ($format) {
691
            return date($format, strtotime($value));
692
        });
693
    }
694
695
    /**
696
     * Display column as boolean , `✓` for true, and `✗` for false.
697
     *
698
     * @param array $map
699
     * @param bool  $default
700
     *
701
     * @return $this
702
     */
703
    public function bool(array $map = [], $default = false)
704
    {
705
        return $this->display(function ($value) use ($map, $default) {
706
            $bool = empty($map) ? boolval($value) : Arr::get($map, $value, $default);
707
708
            return $bool ? '<i class="fa fa-check text-green"></i>' : '<i class="fa fa-close text-red"></i>';
709
        });
710
    }
711
712
    /**
713
     * Display column using a grid row action.
714
     *
715
     * @param string $action
716
     *
717
     * @return Column
718
     */
719
    public function action($action)
720
    {
721
        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...
722
            throw new \InvalidArgumentException("Action class [$action] must be sub-class of [Encore\Admin\Actions\GridAction]");
723
        }
724
725
        $grid = $this->grid;
726
727
        return $this->display(function ($value, $column) use ($action, $grid) {
728
            /** @var RowAction $action */
729
            $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...
730
731
            return $action->setGrid($grid)
732
                ->setColumn($column)
733
                ->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...
734
        });
735
    }
736
737
    /**
738
     * Add a `dot` before column text.
739
     *
740
     * @param array $options
741
     * @param string $default
742
     * @return Column
743
     */
744
    public function dot($options = [], $default = '')
745
    {
746
        return $this->prefix(function ($_, $original) use ($options, $default) {
747
            if (is_null($original)) {
748
                $style = $default;
749
            } else {
750
                $style = Arr::get($options, $original);
751
            }
752
753
            return "<span class=\"label-{$style}\" style='width: 8px;height: 8px;padding: 0;border-radius: 50%;display: inline-block;'></span>";
754
        }, '&nbsp;&nbsp;');
755
    }
756
757
    /**
758
     * If has display callbacks.
759
     *
760
     * @return bool
761
     */
762
    protected function hasDisplayCallbacks()
763
    {
764
        return !empty($this->displayCallbacks);
765
    }
766
767
    /**
768
     * Call all of the "display" callbacks column.
769
     *
770
     * @param mixed $value
771
     * @param int   $key
772
     *
773
     * @return mixed
774
     */
775
    protected function callDisplayCallbacks($value, $key)
776
    {
777
        foreach ($this->displayCallbacks as $callback) {
778
            $previous = $value;
779
780
            $callback = $this->bindOriginalRowModel($callback, $key);
781
            $value = call_user_func_array($callback, [$value, $this]);
782
783
            if (($value instanceof static) &&
784
                ($last = array_pop($this->displayCallbacks))
785
            ) {
786
                $last = $this->bindOriginalRowModel($last, $key);
787
                $value = call_user_func($last, $previous);
788
            }
789
        }
790
791
        return $value;
792
    }
793
794
    /**
795
     * Set original grid data to column.
796
     *
797
     * @param Closure $callback
798
     * @param int     $key
799
     *
800
     * @return Closure
801
     */
802
    protected function bindOriginalRowModel(Closure $callback, $key)
803
    {
804
        $rowModel = static::$originalGridModels[$key];
805
806
        return $callback->bindTo($rowModel);
807
    }
808
809
    /**
810
     * Fill all data to every column.
811
     *
812
     * @param array $data
813
     *
814
     * @return mixed
815
     */
816
    public function fill(array $data)
817
    {
818
        foreach ($data as $key => &$row) {
819
            $this->original = $value = Arr::get($row, $this->name);
820
821
            $value = $this->htmlEntityEncode($value);
822
823
            Arr::set($row, $this->name, $value);
824
825
            if ($this->isDefinedColumn()) {
826
                $this->useDefinedColumn();
827
            }
828
829
            if ($this->hasDisplayCallbacks()) {
830
                $value = $this->callDisplayCallbacks($this->original, $key);
831
                Arr::set($row, $this->name, $value);
832
            }
833
        }
834
835
        return $data;
836
    }
837
838
    /**
839
     * If current column is a defined column.
840
     *
841
     * @return bool
842
     */
843
    protected function isDefinedColumn()
844
    {
845
        return array_key_exists($this->name, static::$defined);
846
    }
847
848
    /**
849
     * Use a defined column.
850
     *
851
     * @throws \Exception
852
     */
853
    protected function useDefinedColumn()
854
    {
855
        // clear all display callbacks.
856
        $this->displayCallbacks = [];
857
858
        $class = static::$defined[$this->name];
859
860
        if ($class instanceof Closure) {
861
            $this->display($class);
862
863
            return;
864
        }
865
866
        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...
867
            throw new \Exception("Invalid column definition [$class]");
868
        }
869
870
        $grid = $this->grid;
871
        $column = $this;
872
873
        $this->display(function ($value) use ($grid, $column, $class) {
874
            /** @var AbstractDisplayer $definition */
875
            $definition = new $class($value, $grid, $column, $this);
876
877
            return $definition->display();
878
        });
879
    }
880
881
    /**
882
     * Convert characters to HTML entities recursively.
883
     *
884
     * @param array|string $item
885
     *
886
     * @return mixed
887
     */
888
    protected function htmlEntityEncode($item)
889
    {
890
        if (is_array($item)) {
891
            array_walk_recursive($item, function (&$value) {
892
                $value = htmlentities($value);
893
            });
894
        } else {
895
            $item = htmlentities($item);
896
        }
897
898
        return $item;
899
    }
900
901
    /**
902
     * Find a displayer to display column.
903
     *
904
     * @param string $abstract
905
     * @param array  $arguments
906
     *
907
     * @return $this
908
     */
909
    protected function resolveDisplayer($abstract, $arguments)
910
    {
911
        if (array_key_exists($abstract, static::$displayers)) {
912
            return $this->callBuiltinDisplayer(static::$displayers[$abstract], $arguments);
913
        }
914
915
        return $this->callSupportDisplayer($abstract, $arguments);
916
    }
917
918
    /**
919
     * Call Illuminate/Support displayer.
920
     *
921
     * @param string $abstract
922
     * @param array  $arguments
923
     *
924
     * @return $this
925
     */
926
    protected function callSupportDisplayer($abstract, $arguments)
927
    {
928
        return $this->display(function ($value) use ($abstract, $arguments) {
929
            if (is_array($value) || $value instanceof Arrayable) {
930
                return call_user_func_array([collect($value), $abstract], $arguments);
931
            }
932
933
            if (is_string($value)) {
934
                return call_user_func_array([Str::class, $abstract], array_merge([$value], $arguments));
935
            }
936
937
            return $value;
938
        });
939
    }
940
941
    /**
942
     * Call Builtin displayer.
943
     *
944
     * @param string $abstract
945
     * @param array  $arguments
946
     *
947
     * @return $this
948
     */
949
    protected function callBuiltinDisplayer($abstract, $arguments)
950
    {
951
        if ($abstract instanceof Closure) {
952
            return $this->display(function ($value) use ($abstract, $arguments) {
953
                return $abstract->call($this, ...array_merge([$value], $arguments));
954
            });
955
        }
956
957
        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...
958
            $grid = $this->grid;
959
            $column = $this;
960
961 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...
962
                /** @var AbstractDisplayer $displayer */
963
                $displayer = new $abstract($value, $grid, $column, $this);
964
965
                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...
966
            });
967
        }
968
969
        return $this;
970
    }
971
972
    /**
973
     * Passes through all unknown calls to builtin displayer or supported displayer.
974
     *
975
     * Allow fluent calls on the Column object.
976
     *
977
     * @param string $method
978
     * @param array  $arguments
979
     *
980
     * @return $this
981
     */
982
    public function __call($method, $arguments)
983
    {
984
        if ($this->isRelation() && !$this->relationColumn) {
985
            $this->name = "{$this->relation}.$method";
986
            $this->label = $this->formatLabel($arguments[0] ?? null);
987
988
            $this->relationColumn = $method;
989
990
            return $this;
991
        }
992
993
        return $this->resolveDisplayer($method, $arguments);
994
    }
995
}
996