Completed
Push — master ( e8f4f9...613029 )
by Song
02:23
created

Field::callExtendedField()   B

Complexity

Conditions 8
Paths 13

Size

Total Lines 38

Duplication

Lines 0
Ratio 0 %

Importance

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