Completed
Push — master ( ce3da7...ddbc6d )
by Song
02:21
created

Column::replace()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 1
nop 1
dl 0
loc 10
rs 9.9332
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\Admin;
8
use Encore\Admin\Grid;
9
use Encore\Admin\Grid\Displayers\AbstractDisplayer;
10
use Illuminate\Contracts\Support\Arrayable;
11
use Illuminate\Database\Eloquent\Model;
0 ignored issues
show
Bug introduced by
This use statement conflicts with another class in this namespace, Encore\Admin\Grid\Model.

Let’s assume that you have a directory layout like this:

.
|-- OtherDir
|   |-- Bar.php
|   `-- Foo.php
`-- SomeDir
    `-- Foo.php

and let’s assume the following content of Bar.php:

// Bar.php
namespace OtherDir;

use SomeDir\Foo; // This now conflicts the class OtherDir\Foo

If both files OtherDir/Foo.php and SomeDir/Foo.php are loaded in the same runtime, you will see a PHP error such as the following:

PHP Fatal error:  Cannot use SomeDir\Foo as Foo because the name is already in use in OtherDir/Foo.php

However, as OtherDir/Foo.php does not necessarily have to be loaded and the error is only triggered if it is loaded before OtherDir/Bar.php, this problem might go unnoticed for a while. In order to prevent this error from surfacing, you must import the namespace with a different alias:

// Bar.php
namespace OtherDir;

use SomeDir\Foo as SomeDirFoo; // There is no conflict anymore.
Loading history...
12
use Illuminate\Support\Arr;
13
use Illuminate\Support\Collection;
14
use Illuminate\Support\Str;
15
16
/**
17
 * Class Column.
18
 *
19
 * @method Displayers\Editable      editable()
20
 * @method Displayers\SwitchDisplay switch ($states = [])
21
 * @method Displayers\SwitchGroup   switchGroup($columns = [], $states = [])
22
 * @method Displayers\Select        select($options = [])
23
 * @method Displayers\Image         image($server = '', $width = 200, $height = 200)
24
 * @method Displayers\Label         label($style = 'success')
25
 * @method Displayers\Button        button($style = null)
26
 * @method Displayers\Link          link($href = '', $target = '_blank')
27
 * @method Displayers\Badge         badge($style = 'red')
28
 * @method Displayers\ProgressBar   progressBar($style = 'primary', $size = 'sm', $max = 100)
29
 * @method Displayers\Radio         radio($options = [])
30
 * @method Displayers\Checkbox      checkbox($options = [])
31
 * @method Displayers\Orderable     orderable($column, $label = '')
32
 * @method Displayers\Table         table($titles = [])
33
 * @method Displayers\Expand        expand($callback = null)
34
 * @method Displayers\Modal         modal($callback = null)
35
 * @method Displayers\Carousel      carousel(int $width = 300, int $height = 200, $server = '')
36
 * @method Displayers\Download      download($server = '')
37
 */
38
class Column
39
{
40
    const SELECT_COLUMN_NAME = '__row_selector__';
41
42
    const ACTION_COLUMN_NAME = '__actions__';
43
44
    /**
45
     * @var Grid
46
     */
47
    protected $grid;
48
49
    /**
50
     * Name of column.
51
     *
52
     * @var string
53
     */
54
    protected $name;
55
56
    /**
57
     * Label of column.
58
     *
59
     * @var string
60
     */
61
    protected $label;
62
63
    /**
64
     * Original value of column.
65
     *
66
     * @var mixed
67
     */
68
    protected $original;
69
70
    /**
71
     * Is column sortable.
72
     *
73
     * @var bool
74
     */
75
    protected $sortable = false;
76
77
    /**
78
     * Sort arguments.
79
     *
80
     * @var array
81
     */
82
    protected $sort;
83
84
    /**
85
     * Help message.
86
     *
87
     * @var string
88
     */
89
    protected $help = '';
90
91
    /**
92
     * Cast Name.
93
     *
94
     * @var array
95
     */
96
    protected $cast;
97
98
    /**
99
     * Attributes of column.
100
     *
101
     * @var array
102
     */
103
    protected $attributes = [];
104
105
    /**
106
     * Relation name.
107
     *
108
     * @var bool
109
     */
110
    protected $relation = false;
111
112
    /**
113
     * Relation column.
114
     *
115
     * @var string
116
     */
117
    protected $relationColumn;
118
119
    /**
120
     * Original grid data.
121
     *
122
     * @var Collection
123
     */
124
    protected static $originalGridModels;
125
126
    /**
127
     * @var []Closure
128
     */
129
    protected $displayCallbacks = [];
130
131
    /**
132
     * Displayers for grid column.
133
     *
134
     * @var array
135
     */
136
    public static $displayers = [];
137
138
    /**
139
     * Defined columns.
140
     *
141
     * @var array
142
     */
143
    public static $defined = [];
144
145
    /**
146
     * @var array
147
     */
148
    protected static $htmlAttributes = [];
149
150
    /**
151
     * @var Model
152
     */
153
    protected static $model;
154
155
    /**
156
     * @param string $name
157
     * @param string $label
158
     */
159
    public function __construct($name, $label)
160
    {
161
        $this->name = $name;
162
163
        $this->label = $this->formatLabel($label);
164
    }
165
166
    /**
167
     * Extend column displayer.
168
     *
169
     * @param $name
170
     * @param $displayer
171
     */
172
    public static function extend($name, $displayer)
173
    {
174
        static::$displayers[$name] = $displayer;
175
    }
176
177
    /**
178
     * Define a column globally.
179
     *
180
     * @param string $name
181
     * @param mixed  $definition
182
     */
183
    public static function define($name, $definition)
184
    {
185
        static::$defined[$name] = $definition;
186
    }
187
188
    /**
189
     * Set grid instance for column.
190
     *
191
     * @param Grid $grid
192
     */
193
    public function setGrid(Grid $grid)
194
    {
195
        $this->grid = $grid;
196
197
        $this->setModel($grid->model()->eloquent());
198
    }
199
200
    /**
201
     * Set model for column.
202
     *
203
     * @param $model
204
     */
205
    public function setModel($model)
206
    {
207
        if (is_null(static::$model) && ($model instanceof Model)) {
208
            static::$model = $model->newInstance();
209
        }
210
    }
211
212
    /**
213
     * Set original data for column.
214
     *
215
     * @param Collection $collection
216
     */
217
    public static function setOriginalGridModels(Collection $collection)
218
    {
219
        static::$originalGridModels = $collection;
220
    }
221
222
    /**
223
     * Set column attributes.
224
     *
225
     * @param array $attributes
226
     *
227
     * @return $this
228
     */
229
    public function setAttributes($attributes = [])
230
    {
231
        static::$htmlAttributes[$this->name] = $attributes;
232
233
        return $this;
234
    }
235
236
    /**
237
     * Get column attributes.
238
     *
239
     * @param string $name
240
     *
241
     * @return mixed
242
     */
243
    public static function getAttributes($name)
244
    {
245
        return Arr::get(static::$htmlAttributes, $name, '');
246
    }
247
248
    /**
249
     * Set style of this column.
250
     *
251
     * @param string $style
252
     *
253
     * @return $this
254
     */
255
    public function style($style)
256
    {
257
        return $this->setAttributes(compact('style'));
258
    }
259
260
    /**
261
     * Set the width of column.
262
     *
263
     * @param int $width
264
     *
265
     * @return $this
266
     */
267
    public function width(int $width)
268
    {
269
        return $this->style("width: {$width}px;");
270
    }
271
272
    /**
273
     * Set the color of column.
274
     *
275
     * @param string $color
276
     *
277
     * @return $this
278
     */
279
    public function color($color)
280
    {
281
        return $this->style("color:$color;");
282
    }
283
284
    /**
285
     * Get original column value.
286
     *
287
     * @return mixed
288
     */
289
    public function getOriginal()
290
    {
291
        return $this->original;
292
    }
293
294
    /**
295
     * Get name of this column.
296
     *
297
     * @return mixed
298
     */
299
    public function getName()
300
    {
301
        return $this->name;
302
    }
303
304
    /**
305
     * Format label.
306
     *
307
     * @param $label
308
     *
309
     * @return mixed
310
     */
311
    protected function formatLabel($label)
312
    {
313
        if ($label) {
314
            return $label;
315
        }
316
317
        $label = ucfirst($this->name);
318
319
        return __(str_replace(['.', '_'], ' ', $label));
320
    }
321
322
    /**
323
     * Get label of the column.
324
     *
325
     * @return mixed
326
     */
327
    public function getLabel()
328
    {
329
        return $this->label;
330
    }
331
332
    /**
333
     * Set relation.
334
     *
335
     * @param string $relation
336
     * @param string $relationColumn
337
     *
338
     * @return $this
339
     */
340
    public function setRelation($relation, $relationColumn = null)
341
    {
342
        $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...
343
        $this->relationColumn = $relationColumn;
344
345
        return $this;
346
    }
347
348
    /**
349
     * If this column is relation column.
350
     *
351
     * @return bool
352
     */
353
    protected function isRelation()
354
    {
355
        return (bool) $this->relation;
356
    }
357
358
    /**
359
     * Set sort value.
360
     *
361
     * @param bool $sort
362
     *
363
     * @return $this
364
     */
365
    public function sort($sort)
366
    {
367
        $this->sortable = $sort;
368
369
        return $this;
370
    }
371
372
    /**
373
     * Mark this column as sortable.
374
     *
375
     * @return $this
376
     */
377
    public function sortable()
378
    {
379
        return $this->sort(true);
380
    }
381
382
    /**
383
     * Set cast name for sortable.
384
     *
385
     * @return $this
386
     */
387
    public function cast($cast)
388
    {
389
        $this->cast = $cast;
390
391
        return $this;
392
    }
393
394
    /**
395
     * Add a display callback.
396
     *
397
     * @param Closure $callback
398
     *
399
     * @return $this
400
     */
401
    public function display(Closure $callback)
402
    {
403
        $this->displayCallbacks[] = $callback;
404
405
        return $this;
406
    }
407
408
    /**
409
     * Display using display abstract.
410
     *
411
     * @param string $abstract
412
     * @param array  $arguments
413
     *
414
     * @return $this
415
     */
416
    public function displayUsing($abstract, $arguments = [])
417
    {
418
        $grid = $this->grid;
419
420
        $column = $this;
421
422 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...
423
            /** @var AbstractDisplayer $displayer */
424
            $displayer = new $abstract($value, $grid, $column, $this);
425
426
            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...
427
        });
428
    }
429
430
    /**
431
     * Display column using array value map.
432
     *
433
     * @param array $values
434
     * @param null  $default
435
     *
436
     * @return $this
437
     */
438 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...
439
    {
440
        return $this->display(function ($value) use ($values, $default) {
441
            if (is_null($value)) {
442
                return $default;
443
            }
444
445
            return Arr::get($values, $value, $default);
446
        });
447
    }
448
449
    /**
450
     * Replace output value with giving map.
451
     *
452
     * @param array $replacements
453
     *
454
     * @return $this
455
     */
456
    public function replace(array $replacements)
457
    {
458
        return $this->display(function ($value) use ($replacements) {
459
            if (isset($replacements[$value])) {
460
                return $replacements[$value];
461
            }
462
463
            return $value;
464
        });
465
    }
466
467
    /**
468
     * Render this column with the given view.
469
     *
470
     * @param string $view
471
     *
472
     * @return $this
473
     */
474
    public function view($view)
475
    {
476
        return $this->display(function ($value) use ($view) {
477
            $model = $this;
478
479
            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...
480
        });
481
    }
482
483
    /**
484
     * Hide this column by default.
485
     *
486
     * @return $this
487
     */
488
    public function hide()
489
    {
490
        $this->grid->hideColumns($this->getName());
491
492
        return $this;
493
    }
494
495
    /**
496
     * Add column to total-row.
497
     *
498
     * @param null $display
499
     *
500
     * @return $this
501
     */
502
    public function totalRow($display = null)
503
    {
504
        $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...
505
506
        return $this;
507
    }
508
509
    /**
510
     * Convert file size to a human readable format like `100mb`.
511
     *
512
     * @return $this
513
     */
514
    public function filesize()
515
    {
516
        return $this->display(function ($value) {
517
            return file_size($value);
518
        });
519
    }
520
521
    /**
522
     * Display the fields in the email format as gavatar.
523
     *
524
     * @param int $size
525
     *
526
     * @return $this
527
     */
528
    public function gravatar($size = 30)
529
    {
530
        return $this->display(function ($value) use ($size) {
531
            $src = sprintf(
532
                'https://www.gravatar.com/avatar/%s?s=%d',
533
                md5(strtolower($value)),
534
                $size
535
            );
536
537
            return "<img src='$src' class='img img-circle'/>";
538
        });
539
    }
540
541
    /**
542
     * Display field as a loading icon.
543
     *
544
     * @param array $values
545
     * @param array $others
546
     *
547
     * @return $this
548
     */
549
    public function loading($values = [], $others = [])
550
    {
551
        return $this->display(function ($value) use ($values, $others) {
552
553
            $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...
554
555
            if (in_array($value, $values)) {
556
                return '<i class="fa fa-refresh fa-spin text-primary"></i>';
557
            }
558
559
            return Arr::get($others, $value, $value);
560
        });
561
    }
562
563
    /**
564
     * Display column as an font-awesome icon based on it's value.
565
     *
566
     * @param array $setting
567
     * @param string $default
568
     *
569
     * @return $this
570
     */
571
    public function icon(array $setting, $default = '')
572
    {
573
        return $this->display(function ($value) use ($setting, $default) {
574
575
            $fa = '';
576
577
            if (isset($setting[$value])) {
578
                $fa = $setting[$value];
579
            } elseif ($default) {
580
                $fa = $default;
581
            }
582
583
            return "<i class=\"fa fa-{$fa}\"></i>";
584
        });
585
    }
586
587
    /**
588
     * Return a human readable format time.
589
     *
590
     * @param null $locale
591
     *
592
     * @return $this
593
     */
594
    public function diffForHumans($locale = null)
595
    {
596
        if ($locale) {
597
            Carbon::setLocale($locale);
598
        }
599
600
        return $this->display(function ($value) {
601
            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...
602
        });
603
    }
604
605
    /**
606
     * If has display callbacks.
607
     *
608
     * @return bool
609
     */
610
    protected function hasDisplayCallbacks()
611
    {
612
        return !empty($this->displayCallbacks);
613
    }
614
615
    /**
616
     * Call all of the "display" callbacks column.
617
     *
618
     * @param mixed $value
619
     * @param int   $key
620
     *
621
     * @return mixed
622
     */
623
    protected function callDisplayCallbacks($value, $key)
624
    {
625
        foreach ($this->displayCallbacks as $callback) {
626
            $previous = $value;
627
628
            $callback = $this->bindOriginalRowModel($callback, $key);
629
            $value = call_user_func_array($callback, [$value, $this]);
630
631
            if (($value instanceof static) &&
632
                ($last = array_pop($this->displayCallbacks))
633
            ) {
634
                $last = $this->bindOriginalRowModel($last, $key);
635
                $value = call_user_func($last, $previous);
636
            }
637
        }
638
639
        return $value;
640
    }
641
642
    /**
643
     * Set original grid data to column.
644
     *
645
     * @param Closure $callback
646
     * @param int     $key
647
     *
648
     * @return Closure
649
     */
650
    protected function bindOriginalRowModel(Closure $callback, $key)
651
    {
652
        $rowModel = static::$originalGridModels[$key];
653
654
        return $callback->bindTo($rowModel);
655
    }
656
657
    /**
658
     * Fill all data to every column.
659
     *
660
     * @param array $data
661
     *
662
     * @return mixed
663
     */
664
    public function fill(array $data)
665
    {
666
        foreach ($data as $key => &$row) {
667
            $this->original = $value = Arr::get($row, $this->name);
668
669
            $value = $this->htmlEntityEncode($value);
670
671
            Arr::set($row, $this->name, $value);
672
673
            if ($this->isDefinedColumn()) {
674
                $this->useDefinedColumn();
675
            }
676
677
            if ($this->hasDisplayCallbacks()) {
678
                $value = $this->callDisplayCallbacks($this->original, $key);
679
                Arr::set($row, $this->name, $value);
680
            }
681
        }
682
683
        return $data;
684
    }
685
686
    /**
687
     * If current column is a defined column.
688
     *
689
     * @return bool
690
     */
691
    protected function isDefinedColumn()
692
    {
693
        return array_key_exists($this->name, static::$defined);
694
    }
695
696
    /**
697
     * Use a defined column.
698
     *
699
     * @throws \Exception
700
     */
701
    protected function useDefinedColumn()
702
    {
703
        // clear all display callbacks.
704
        $this->displayCallbacks = [];
705
706
        $class = static::$defined[$this->name];
707
708
        if ($class instanceof Closure) {
709
            $this->display($class);
710
711
            return;
712
        }
713
714
        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...
715
            throw new \Exception("Invalid column definition [$class]");
716
        }
717
718
        $grid = $this->grid;
719
        $column = $this;
720
721
        $this->display(function ($value) use ($grid, $column, $class) {
722
            /** @var AbstractDisplayer $definition */
723
            $definition = new $class($value, $grid, $column, $this);
724
725
            return $definition->display();
726
        });
727
    }
728
729
    /**
730
     * Convert characters to HTML entities recursively.
731
     *
732
     * @param array|string $item
733
     *
734
     * @return mixed
735
     */
736
    protected function htmlEntityEncode($item)
737
    {
738
        if (is_array($item)) {
739
            array_walk_recursive($item, function (&$value) {
740
                $value = htmlentities($value);
741
            });
742
        } else {
743
            $item = htmlentities($item);
744
        }
745
746
        return $item;
747
    }
748
749
    /**
750
     * Create the column sorter.
751
     *
752
     * @return string
753
     */
754
    public function sorter()
755
    {
756
        if (!$this->sortable) {
757
            return '';
758
        }
759
760
        $icon = 'fa-sort';
761
        $type = 'desc';
762
763
        if ($this->isSorted()) {
764
            $type = $this->sort['type'] == 'desc' ? 'asc' : 'desc';
765
            $icon .= "-amount-{$this->sort['type']}";
766
        }
767
768
        // set sort value
769
        $sort = ['column' => $this->name, 'type' => $type];
770
        if (isset($this->cast)) {
771
            $sort['cast'] = $this->cast;
772
        }
773
774
        $query = app('request')->all();
775
        $query = array_merge($query, [$this->grid->model()->getSortName() => $sort]);
776
777
        $url = url()->current().'?'.http_build_query($query);
778
779
        return "<a class=\"fa fa-fw $icon\" href=\"$url\"></a>";
780
    }
781
782
    /**
783
     * Determine if this column is currently sorted.
784
     *
785
     * @return bool
786
     */
787
    protected function isSorted()
788
    {
789
        $this->sort = app('request')->get($this->grid->model()->getSortName());
790
791
        if (empty($this->sort)) {
792
            return false;
793
        }
794
795
        return isset($this->sort['column']) && $this->sort['column'] == $this->name;
796
    }
797
798
    /**
799
     * Set help message for column.
800
     *
801
     * @param string $help
802
     *
803
     * @return $this|string
804
     */
805
    public function help($help = '')
806
    {
807
        if (!empty($help)) {
808
            $this->help = $help;
809
810
            return $this;
811
        }
812
813
        if (empty($this->help)) {
814
            return '';
815
        }
816
817
        Admin::script("$('.column-help').popover();");
818
819
        $data = [
820
            'container' => 'body',
821
            'toggle'    => 'popover',
822
            'trigger'   => 'hover',
823
            'placement' => 'bottom',
824
            'content'   => $this->help,
825
        ];
826
827
        $data = collect($data)->map(function ($val, $key) {
828
            return "data-{$key}=\"{$val}\"";
829
        })->implode(' ');
830
831
        return <<<HELP
832
<a href="javascript:void(0);" class="column-help" {$data}>
833
    <i class="fa fa-question-circle"></i>
834
</a>
835
HELP;
836
    }
837
838
    /**
839
     * Find a displayer to display column.
840
     *
841
     * @param string $abstract
842
     * @param array  $arguments
843
     *
844
     * @return $this
845
     */
846
    protected function resolveDisplayer($abstract, $arguments)
847
    {
848
        if (array_key_exists($abstract, static::$displayers)) {
849
            return $this->callBuiltinDisplayer(static::$displayers[$abstract], $arguments);
850
        }
851
852
        return $this->callSupportDisplayer($abstract, $arguments);
853
    }
854
855
    /**
856
     * Call Illuminate/Support displayer.
857
     *
858
     * @param string $abstract
859
     * @param array  $arguments
860
     *
861
     * @return $this
862
     */
863
    protected function callSupportDisplayer($abstract, $arguments)
864
    {
865
        return $this->display(function ($value) use ($abstract, $arguments) {
866
            if (is_array($value) || $value instanceof Arrayable) {
867
                return call_user_func_array([collect($value), $abstract], $arguments);
868
            }
869
870
            if (is_string($value)) {
871
                return call_user_func_array([Str::class, $abstract], array_merge([$value], $arguments));
872
            }
873
874
            return $value;
875
        });
876
    }
877
878
    /**
879
     * Call Builtin displayer.
880
     *
881
     * @param string $abstract
882
     * @param array  $arguments
883
     *
884
     * @return $this
885
     */
886
    protected function callBuiltinDisplayer($abstract, $arguments)
887
    {
888
        if ($abstract instanceof Closure) {
889
            return $this->display(function ($value) use ($abstract, $arguments) {
890
                return $abstract->call($this, ...array_merge([$value], $arguments));
891
            });
892
        }
893
894
        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...
895
            $grid = $this->grid;
896
            $column = $this;
897
898 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...
899
                /** @var AbstractDisplayer $displayer */
900
                $displayer = new $abstract($value, $grid, $column, $this);
901
902
                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...
903
            });
904
        }
905
906
        return $this;
907
    }
908
909
    /**
910
     * Passes through all unknown calls to builtin displayer or supported displayer.
911
     *
912
     * Allow fluent calls on the Column object.
913
     *
914
     * @param string $method
915
     * @param array  $arguments
916
     *
917
     * @return $this
918
     */
919
    public function __call($method, $arguments)
920
    {
921
        if ($this->isRelation() && !$this->relationColumn) {
922
            $this->name = "{$this->relation}.$method";
923
            $this->label = $this->formatLabel($arguments[0] ?? null);
924
925
            $this->relationColumn = $method;
926
927
            return $this;
928
        }
929
930
        return $this->resolveDisplayer($method, $arguments);
931
    }
932
}
933