Completed
Push — master ( fe01b0...4ca583 )
by Song
02:33
created

Field::label()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 12

Duplication

Lines 12
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
nc 1
nop 1
dl 12
loc 12
rs 9.8666
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Show;
4
5
use Encore\Admin\Show;
6
use Encore\Admin\Widgets\Carousel;
7
use Illuminate\Contracts\Support\Arrayable;
8
use Illuminate\Contracts\Support\Renderable;
9
use Illuminate\Database\Eloquent\Model;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Collection;
12
use Illuminate\Support\Facades\File;
13
use Illuminate\Support\Facades\Storage;
14
use Illuminate\Support\Str;
15
use Illuminate\Support\Traits\Macroable;
16
17
class Field implements Renderable
18
{
19
    use Macroable {
20
        __call as macroCall;
21
    }
22
23
    /**
24
     * @var string
25
     */
26
    protected $view = 'admin::show.field';
27
28
    /**
29
     * Name of column.
30
     *
31
     * @var string
32
     */
33
    protected $name;
34
35
    /**
36
     * Label of column.
37
     *
38
     * @var string
39
     */
40
    protected $label;
41
42
    /**
43
     * Width for label and field.
44
     *
45
     * @var array
46
     */
47
    protected $width = [
48
        'label' => 2,
49
        'field' => 8,
50
    ];
51
52
    /**
53
     * Escape field value or not.
54
     *
55
     * @var bool
56
     */
57
    protected $escape = true;
58
59
    /**
60
     * Field value.
61
     *
62
     * @var mixed
63
     */
64
    protected $value;
65
66
    /**
67
     * @var Collection
68
     */
69
    protected $showAs = [];
70
71
    /**
72
     * Parent show instance.
73
     *
74
     * @var Show
75
     */
76
    protected $parent;
77
78
    /**
79
     * Relation name.
80
     *
81
     * @var string
82
     */
83
    protected $relation;
84
85
    /**
86
     * If show contents in box.
87
     *
88
     * @var bool
89
     */
90
    public $border = true;
91
92
    /**
93
     * @var array
94
     */
95
    protected $fileTypes = [
96
        'image'      => 'png|jpg|jpeg|tmp|gif',
97
        'word'       => 'doc|docx',
98
        'excel'      => 'xls|xlsx|csv',
99
        'powerpoint' => 'ppt|pptx',
100
        'pdf'        => 'pdf',
101
        'code'       => 'php|js|java|python|ruby|go|c|cpp|sql|m|h|json|html|aspx',
102
        'archive'    => 'zip|tar\.gz|rar|rpm',
103
        'txt'        => 'txt|pac|log|md',
104
        'audio'      => 'mp3|wav|flac|3pg|aa|aac|ape|au|m4a|mpc|ogg',
105
        'video'      => 'mkv|rmvb|flv|mp4|avi|wmv|rm|asf|mpeg',
106
    ];
107
108
    /**
109
     * Field constructor.
110
     *
111
     * @param string $name
112
     * @param string $label
113
     */
114
    public function __construct($name = '', $label = '')
115
    {
116
        $this->name = $name;
117
118
        $this->label = $this->formatLabel($label);
119
120
        $this->showAs = new Collection();
121
    }
122
123
    /**
124
     * Set parent show instance.
125
     *
126
     * @param Show $show
127
     *
128
     * @return $this
129
     */
130
    public function setParent(Show $show)
131
    {
132
        $this->parent = $show;
133
134
        return $this;
135
    }
136
137
    /**
138
     * Get name of this column.
139
     *
140
     * @return mixed
141
     */
142
    public function getName()
143
    {
144
        return $this->name;
145
    }
146
147
    /**
148
     * Format label.
149
     *
150
     * @param $label
151
     *
152
     * @return mixed
153
     */
154
    protected function formatLabel($label)
155
    {
156
        $label = $label ?: ucfirst($this->name);
157
158
        return str_replace(['.', '_'], ' ', $label);
159
    }
160
161
    /**
162
     * Get label of the column.
163
     *
164
     * @return mixed
165
     */
166
    public function getLabel()
167
    {
168
        return $this->label;
169
    }
170
171
    /**
172
     * Field display callback.
173
     *
174
     * @param callable $callable
175
     *
176
     * @return $this
177
     */
178
    public function as(callable $callable)
179
    {
180
        $this->showAs->push($callable);
181
182
        return $this;
183
    }
184
185
    /**
186
     * Display field using array value map.
187
     *
188
     * @param array $values
189
     * @param null  $default
190
     *
191
     * @return $this
192
     */
193 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...
194
    {
195
        return $this->as(function ($value) use ($values, $default) {
196
            if (is_null($value)) {
197
                return $default;
198
            }
199
200
            return Arr::get($values, $value, $default);
201
        });
202
    }
203
204
    /**
205
     * Show field as a image.
206
     *
207
     * @param string $server
208
     * @param int    $width
209
     * @param int    $height
210
     *
211
     * @return $this
212
     */
213
    public function image($server = '', $width = 200, $height = 200)
214
    {
215
        return $this->unescape()->as(function ($images) use ($server, $width, $height) {
216
            return collect($images)->map(function ($path) use ($server, $width, $height) {
217
                if (empty($path)) {
218
                    return '';
219
                }
220
221 View Code Duplication
                if (url()->isValidUrl($path)) {
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...
222
                    $src = $path;
223
                } elseif ($server) {
224
                    $src = $server.$path;
225
                } else {
226
                    $disk = config('admin.upload.disk');
227
228
                    if (config("filesystems.disks.{$disk}")) {
229
                        $src = Storage::disk($disk)->url($path);
230
                    } else {
231
                        return '';
232
                    }
233
                }
234
235
                return "<img src='$src' style='max-width:{$width}px;max-height:{$height}px' class='img' />";
236
            })->implode('&nbsp;');
237
        });
238
    }
239
240
    /**
241
     * Show field as a carousel.
242
     *
243
     * @param int    $width
244
     * @param int    $height
245
     * @param string $server
246
     *
247
     * @return Field
248
     */
249
    public function carousel($width = 300, $height = 200, $server = '')
250
    {
251
        return $this->unescape()->as(function ($images) use ($server, $width, $height) {
252
            $items = collect($images)->map(function ($path) use ($server, $width, $height) {
253
                if (empty($path)) {
254
                    return '';
255
                }
256
257 View Code Duplication
                if (url()->isValidUrl($path)) {
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...
258
                    $image = $path;
259
                } elseif ($server) {
260
                    $image = $server.$path;
261
                } else {
262
                    $disk = config('admin.upload.disk');
263
264
                    if (config("filesystems.disks.{$disk}")) {
265
                        $image = Storage::disk($disk)->url($path);
266
                    } else {
267
                        $image = '';
268
                    }
269
                }
270
271
                $caption = '';
272
273
                return compact('image', 'caption');
274
            });
275
276
            return (new Carousel($items))->width($width)->height($height);
277
        });
278
    }
279
280
    /**
281
     * Show field as a file.
282
     *
283
     * @param string $server
284
     * @param bool   $download
285
     *
286
     * @return Field
287
     */
288
    public function file($server = '', $download = true)
289
    {
290
        $field = $this;
291
292
        return $this->unescape()->as(function ($path) use ($server, $download, $field) {
293
            $name = basename($path);
294
295
            $field->border = false;
296
297
            $size = $url = '';
298
299
            if (url()->isValidUrl($path)) {
300
                $url = $path;
301
            } elseif ($server) {
302
                $url = $server.$path;
303
            } else {
304
                $storage = Storage::disk(config('admin.upload.disk'));
305
                if ($storage->exists($path)) {
306
                    $url = $storage->url($path);
307
                    $size = ($storage->size($path) / 1000).'KB';
308
                }
309
            }
310
311
            if (!$url) {
312
                return '';
313
            }
314
315
            $download = $download ? "download='$name'" : '';
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $download, 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...
316
317
            return <<<HTML
318
<ul class="mailbox-attachments clearfix">
319
    <li style="margin-bottom: 0;">
320
      <span class="mailbox-attachment-icon"><i class="fa {$field->getFileIcon($name)}"></i></span>
321
      <div class="mailbox-attachment-info">
322
        <div class="mailbox-attachment-name">
323
            <i class="fa fa-paperclip"></i> {$name}
324
            </div>
325
            <span class="mailbox-attachment-size">
326
              {$size}&nbsp;
327
              <a href="{$url}" class="btn btn-default btn-xs pull-right" target="_blank" $download><i class="fa fa-cloud-download"></i></a>
328
            </span>
329
      </div>
330
    </li>
331
  </ul>
332
HTML;
333
        });
334
    }
335
336
    /**
337
     * Show field as a link.
338
     *
339
     * @param string $href
340
     * @param string $target
341
     *
342
     * @return Field
343
     */
344
    public function link($href = '', $target = '_blank')
345
    {
346
        return $this->unescape()->as(function ($link) use ($href, $target) {
347
            $href = $href ?: $link;
0 ignored issues
show
Bug introduced by
Consider using a different name than the imported variable $href, 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...
348
349
            return "<a href='$href' target='{$target}'>{$link}</a>";
350
        });
351
    }
352
353
    /**
354
     * Show field as labels.
355
     *
356
     * @param string $style
357
     *
358
     * @return Field
359
     */
360 View Code Duplication
    public function label($style = 'success')
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...
361
    {
362
        return $this->unescape()->as(function ($value) use ($style) {
363
            if ($value instanceof Arrayable) {
364
                $value = $value->toArray();
365
            }
366
367
            return collect((array) $value)->map(function ($name) use ($style) {
368
                return "<span class='label label-{$style}'>$name</span>";
369
            })->implode('&nbsp;');
370
        });
371
    }
372
373
    /**
374
     * Show field as badges.
375
     *
376
     * @param string $style
377
     *
378
     * @return Field
379
     */
380 View Code Duplication
    public function badge($style = 'blue')
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...
381
    {
382
        return $this->unescape()->as(function ($value) use ($style) {
383
            if ($value instanceof Arrayable) {
384
                $value = $value->toArray();
385
            }
386
387
            return collect((array) $value)->map(function ($name) use ($style) {
388
                return "<span class='badge bg-{$style}'>$name</span>";
389
            })->implode('&nbsp;');
390
        });
391
    }
392
393
    /**
394
     * Show field as json code.
395
     *
396
     * @return Field
397
     */
398
    public function json()
399
    {
400
        $field = $this;
401
402
        return $this->unescape()->as(function ($value) use ($field) {
403
            $content = json_decode($value, true);
404
405
            if (json_last_error() == 0) {
406
                $field->border = false;
407
408
                return '<pre><code>'.json_encode($content, JSON_PRETTY_PRINT).'</code></pre>';
409
            }
410
411
            return $value;
412
        });
413
    }
414
415
    /**
416
     * Show readable filesize for giving integer size.
417
     *
418
     * @return Field
419
     */
420
    public function filesize()
421
    {
422
        return $this->as(function ($value) {
423
            return file_size($value);
424
        });
425
    }
426
427
    /**
428
     * Get file icon.
429
     *
430
     * @param string $file
431
     *
432
     * @return string
433
     */
434
    public function getFileIcon($file = '')
435
    {
436
        $extension = File::extension($file);
437
438
        foreach ($this->fileTypes as $type => $regex) {
439
            if (preg_match("/^($regex)$/i", $extension) !== 0) {
440
                return "fa-file-{$type}-o";
441
            }
442
        }
443
444
        return 'fa-file-o';
445
    }
446
447
    /**
448
     * Set escape or not for this field.
449
     *
450
     * @param bool $escape
451
     *
452
     * @return $this
453
     */
454
    public function setEscape($escape = true)
455
    {
456
        $this->escape = $escape;
457
458
        return $this;
459
    }
460
461
    /**
462
     * Unescape for this field.
463
     *
464
     * @return Field
465
     */
466
    public function unescape()
467
    {
468
        return $this->setEscape(false);
469
    }
470
471
    /**
472
     * Set value for this field.
473
     *
474
     * @param Model $model
475
     *
476
     * @return $this
477
     */
478
    public function setValue(Model $model)
479
    {
480
        if ($this->relation) {
481
            if (!$relationValue = $model->{$this->relation}) {
482
                return $this;
483
            }
484
485
            $this->value = $relationValue;
486
        } else {
487
            if (Str::contains($this->name, '.')) {
488
                $this->value = $this->getRelationValue($model, $this->name);
489
            } else {
490
                $this->value = $model->getAttribute($this->name);
491
            }
492
        }
493
494
        return $this;
495
    }
496
497
    /**
498
     * Set relation name for this field.
499
     *
500
     * @param string $relation
501
     *
502
     * @return $this
503
     */
504
    public function setRelation($relation)
505
    {
506
        $this->relation = $relation;
507
508
        return $this;
509
    }
510
511
    /**
512
     * @param Model  $model
513
     * @param string $name
514
     * @return mixed
515
     */
516
    protected function getRelationValue($model, $name)
517
    {
518
        list($relation, $key) = explode('.', $name);
519
520
        if ($related = $model->getRelationValue($relation)) {
521
            return $related->getAttribute($key);
522
        }
523
    }
524
525
    /**
526
     * Set width for field and label.
527
     *
528
     * @param int $field
529
     * @param int $label
530
     *
531
     * @return $this
532
     */
533
    public function setWidth($field = 8, $label = 2)
534
    {
535
        $this->width = [
536
            'label' => $label,
537
            'field' => $field,
538
        ];
539
540
        return $this;
541
    }
542
543
    /**
544
     * Call extended field.
545
     *
546
     * @param string|AbstractField|\Closure $abstract
547
     * @param array                         $arguments
548
     *
549
     * @return Field
550
     */
551
    protected function callExtendedField($abstract, $arguments = [])
552
    {
553
        if ($abstract instanceof \Closure) {
554
            return $this->as($abstract);
555
        }
556
557
        if (is_string($abstract) && class_exists($abstract)) {
558
            /** @var AbstractField $extend */
559
            $extend = new $abstract();
560
        }
561
562
        if ($abstract instanceof AbstractField) {
563
            /** @var AbstractField $extend */
564
            $extend = $abstract;
565
        }
566
567
        if (!isset($extend)) {
568
            admin_warning("[$abstract] is not a valid Show field.");
569
570
            return $this;
571
        }
572
573
        if (!$extend->escape) {
574
            $this->unescape();
575
        }
576
577
        $field = $this;
578
579
        return $this->as(function ($value) use ($extend, $field, $arguments) {
580
            if (!$extend->border) {
581
                $field->border = false;
582
            }
583
584
            $extend->setValue($value)->setModel($this);
0 ignored issues
show
Compatibility introduced by
$this of type object<Encore\Admin\Show\Field> is not a sub-type of object<Illuminate\Database\Eloquent\Model>. It seems like you assume a child class of the class Encore\Admin\Show\Field 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...
585
586
            return $extend->render(...$arguments);
0 ignored issues
show
Unused Code introduced by
The call to AbstractField::render() 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...
587
        });
588
    }
589
590
    /**
591
     * @param string $method
592
     * @param array  $arguments
593
     *
594
     * @return $this
595
     */
596
    public function __call($method, $arguments = [])
597
    {
598
        if ($class = Arr::get(Show::$extendedFields, $method)) {
599
            return $this->callExtendedField($class, $arguments);
600
        }
601
602
        if (static::hasMacro($method)) {
603
            return $this->macroCall($method, $arguments);
604
        }
605
606
        if ($this->relation) {
607
            $this->name = $method;
608
            $this->label = $this->formatLabel(Arr::get($arguments, 0));
609
        }
610
611
        return $this;
612
    }
613
614
    /**
615
     * Get all variables passed to field view.
616
     *
617
     * @return array
618
     */
619
    protected function variables()
620
    {
621
        return [
622
            'content'   => $this->value,
623
            'escape'    => $this->escape,
624
            'label'     => $this->getLabel(),
625
            'wrapped'   => $this->border,
626
            'width'     => $this->width,
627
        ];
628
    }
629
630
    /**
631
     * Render this field.
632
     *
633
     * @return string
634
     */
635
    public function render()
636
    {
637
        if ($this->showAs->isNotEmpty()) {
638
            $this->showAs->each(function ($callable) {
639
                $this->value = $callable->call(
640
                    $this->parent->getModel(),
641
                    $this->value
642
                );
643
            });
644
        }
645
646
        return view($this->view, $this->variables());
0 ignored issues
show
Bug Compatibility introduced by
The expression view($this->view, $this->variables()); of type Illuminate\View\View|Ill...\Contracts\View\Factory adds the type Illuminate\Contracts\View\Factory to the return on line 646 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
647
    }
648
}
649