Completed
Push — master ( 814918...02f15d )
by Song
02:23
created

Field::__call()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 2
dl 0
loc 17
rs 9.7
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
     * Get file icon.
414
     *
415
     * @param string $file
416
     *
417
     * @return string
418
     */
419
    public function getFileIcon($file = '')
420
    {
421
        $extension = File::extension($file);
422
423
        foreach ($this->fileTypes as $type => $regex) {
424
            if (preg_match("/^($regex)$/i", $extension) !== 0) {
425
                return "fa-file-{$type}-o";
426
            }
427
        }
428
429
        return 'fa-file-o';
430
    }
431
432
    /**
433
     * Set escape or not for this field.
434
     *
435
     * @param bool $escape
436
     *
437
     * @return $this
438
     */
439
    public function setEscape($escape = true)
440
    {
441
        $this->escape = $escape;
442
443
        return $this;
444
    }
445
446
    /**
447
     * Unescape for this field.
448
     *
449
     * @return Field
450
     */
451
    public function unescape()
452
    {
453
        return $this->setEscape(false);
454
    }
455
456
    /**
457
     * Set value for this field.
458
     *
459
     * @param Model $model
460
     *
461
     * @return $this
462
     */
463
    public function setValue(Model $model)
464
    {
465
        if ($this->relation) {
466
            if (!$relationValue = $model->{$this->relation}) {
467
                return $this;
468
            }
469
470
            $this->value = $relationValue;
471
        } else {
472
            $this->value = $model->getAttribute($this->name);
473
        }
474
475
        return $this;
476
    }
477
478
    /**
479
     * Set relation name for this field.
480
     *
481
     * @param string $relation
482
     *
483
     * @return $this
484
     */
485
    public function setRelation($relation)
486
    {
487
        $this->relation = $relation;
488
489
        return $this;
490
    }
491
492
    /**
493
     * Set width for field and label.
494
     *
495
     * @param int $field
496
     * @param int $label
497
     *
498
     * @return $this
499
     */
500
    public function setWidth($field = 8, $label = 2)
501
    {
502
        $this->width = [
503
            'label' => $label,
504
            'field' => $field,
505
        ];
506
507
        return $this;
508
    }
509
510
    /**
511
     * Call extended field.
512
     *
513
     * @param string|AbstractField|\Closure $abstract
514
     * @param array                         $arguments
515
     *
516
     * @return Field
517
     */
518
    protected function callExtendedField($abstract, $arguments = [])
519
    {
520
        if ($abstract instanceof \Closure) {
521
            return $this->as($abstract);
522
        }
523
524
        if (is_string($abstract) && class_exists($abstract)) {
525
            /** @var AbstractField $extend */
526
            $extend = new $abstract();
527
        }
528
529
        if ($abstract instanceof AbstractField) {
530
            /** @var AbstractField $extend */
531
            $extend = $abstract;
532
        }
533
534
        if (!isset($extend)) {
535
            admin_warning("[$abstract] is not a valid Show field.");
536
537
            return $this;
538
        }
539
540
        if (!$extend->escape) {
541
            $this->unescape();
542
        }
543
544
        $field = $this;
545
546
        return $this->as(function ($value) use ($extend, $field, $arguments) {
547
            if (!$extend->border) {
548
                $field->border = false;
549
            }
550
551
            $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...
552
553
            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...
554
        });
555
    }
556
557
    /**
558
     * @param string $method
559
     * @param array  $arguments
560
     *
561
     * @return $this
562
     */
563
    public function __call($method, $arguments = [])
564
    {
565
        if ($class = Arr::get(Show::$extendedFields, $method)) {
566
            return $this->callExtendedField($class, $arguments);
567
        }
568
569
        if (static::hasMacro($method)) {
570
            return $this->macroCall($method, $arguments);
571
        }
572
573
        if ($this->relation) {
574
            $this->name = $method;
575
            $this->label = $this->formatLabel(Arr::get($arguments, 0));
576
        }
577
578
        return $this;
579
    }
580
581
    /**
582
     * Get all variables passed to field view.
583
     *
584
     * @return array
585
     */
586
    protected function variables()
587
    {
588
        return [
589
            'content'   => $this->value,
590
            'escape'    => $this->escape,
591
            'label'     => $this->getLabel(),
592
            'wrapped'   => $this->border,
593
            'width'     => $this->width,
594
        ];
595
    }
596
597
    /**
598
     * Render this field.
599
     *
600
     * @return string
601
     */
602
    public function render()
603
    {
604
        if ($this->showAs->isNotEmpty()) {
605
            $this->showAs->each(function ($callable) {
606
                $this->value = $callable->call(
607
                    $this->parent->getModel(),
608
                    $this->value
609
                );
610
            });
611
        }
612
613
        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 613 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
614
    }
615
}
616