Completed
Push — master ( f9641d...891d78 )
by Song
02:28
created

Field::creationRules()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10

Duplication

Lines 10
Ratio 100 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 2
dl 10
loc 10
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Form;
4
5
use Closure;
6
use Encore\Admin\Admin;
7
use Encore\Admin\Form;
8
use Illuminate\Contracts\Support\Arrayable;
9
use Illuminate\Contracts\Support\Renderable;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Traits\Macroable;
12
13
/**
14
 * Class Field.
15
 */
16
class Field implements Renderable
17
{
18
    use Macroable;
19
20
    const FILE_DELETE_FLAG = '_file_del_';
21
    const FILE_SORT_FLAG = '_file_sort_';
22
23
    /**
24
     * Element id.
25
     *
26
     * @var array|string
27
     */
28
    protected $id;
29
30
    /**
31
     * Element value.
32
     *
33
     * @var mixed
34
     */
35
    protected $value;
36
37
    /**
38
     * Data of all original columns of value.
39
     *
40
     * @var mixed
41
     */
42
    protected $data;
43
44
    /**
45
     * Field original value.
46
     *
47
     * @var mixed
48
     */
49
    protected $original;
50
51
    /**
52
     * Field default value.
53
     *
54
     * @var mixed
55
     */
56
    protected $default;
57
58
    /**
59
     * Element label.
60
     *
61
     * @var string
62
     */
63
    protected $label = '';
64
65
    /**
66
     * Column name.
67
     *
68
     * @var string|array
69
     */
70
    protected $column = '';
71
72
    /**
73
     * Form element name.
74
     *
75
     * @var string
76
     */
77
    protected $elementName = [];
78
79
    /**
80
     * Form element classes.
81
     *
82
     * @var array
83
     */
84
    protected $elementClass = [];
85
86
    /**
87
     * Variables of elements.
88
     *
89
     * @var array
90
     */
91
    protected $variables = [];
92
93
    /**
94
     * Options for specify elements.
95
     *
96
     * @var array
97
     */
98
    protected $options = [];
99
100
    /**
101
     * Checked for specify elements.
102
     *
103
     * @var array
104
     */
105
    protected $checked = [];
106
107
    /**
108
     * Validation rules.
109
     *
110
     * @var array|\Closure
111
     */
112
    protected $rules = [];
113
114
    /**
115
     * The validation rules for creation.
116
     *
117
     * @var array|\Closure
118
     */
119
    public $creationRules = [];
120
121
    /**
122
     * The validation rules for updates.
123
     *
124
     * @var array|\Closure
125
     */
126
    public $updateRules = [];
127
128
    /**
129
     * @var \Closure
130
     */
131
    protected $validator;
132
133
    /**
134
     * Validation messages.
135
     *
136
     * @var array
137
     */
138
    protected $validationMessages = [];
139
140
    /**
141
     * Css required by this field.
142
     *
143
     * @var array
144
     */
145
    protected static $css = [];
146
147
    /**
148
     * Js required by this field.
149
     *
150
     * @var array
151
     */
152
    protected static $js = [];
153
154
    /**
155
     * Script for field.
156
     *
157
     * @var string
158
     */
159
    protected $script = '';
160
161
    /**
162
     * Element attributes.
163
     *
164
     * @var array
165
     */
166
    protected $attributes = [];
167
168
    /**
169
     * Parent form.
170
     *
171
     * @var Form
172
     */
173
    protected $form = null;
174
175
    /**
176
     * View for field to render.
177
     *
178
     * @var string
179
     */
180
    protected $view = '';
181
182
    /**
183
     * Help block.
184
     *
185
     * @var array
186
     */
187
    protected $help = [];
188
189
    /**
190
     * Key for errors.
191
     *
192
     * @var mixed
193
     */
194
    protected $errorKey;
195
196
    /**
197
     * Placeholder for this field.
198
     *
199
     * @var string|array
200
     */
201
    protected $placeholder;
202
203
    /**
204
     * Width for label and field.
205
     *
206
     * @var array
207
     */
208
    protected $width = [
209
        'label' => 2,
210
        'field' => 8,
211
    ];
212
213
    /**
214
     * If the form horizontal layout.
215
     *
216
     * @var bool
217
     */
218
    protected $horizontal = true;
219
220
    /**
221
     * column data format.
222
     *
223
     * @var \Closure
224
     */
225
    protected $customFormat = null;
226
227
    /**
228
     * @var bool
229
     */
230
    protected $display = true;
231
232
    /**
233
     * @var array
234
     */
235
    protected $labelClass = [];
236
237
    /**
238
     * @var array
239
     */
240
    protected $groupClass = [];
241
242
    /**
243
     * @var \Closure
244
     */
245
    protected $callback;
246
247
    /**
248
     * Field constructor.
249
     *
250
     * @param       $column
251
     * @param array $arguments
252
     */
253
    public function __construct($column, $arguments = [])
254
    {
255
        $this->column = $column;
256
        $this->label  = $this->formatLabel($arguments);
257
        $this->id     = $this->formatId($column);
258
    }
259
260
    /**
261
     * Get assets required by this field.
262
     *
263
     * @return array
264
     */
265
    public static function getAssets()
266
    {
267
        return [
268
            'css' => static::$css,
269
            'js'  => static::$js,
270
        ];
271
    }
272
273
    /**
274
     * Format the field element id.
275
     *
276
     * @param string|array $column
277
     *
278
     * @return string|array
279
     */
280
    protected function formatId($column)
281
    {
282
        return str_replace('.', '_', $column);
283
    }
284
285
    /**
286
     * Format the label value.
287
     *
288
     * @param array $arguments
289
     *
290
     * @return string
291
     */
292
    protected function formatLabel($arguments = [])
293
    {
294
        $column = is_array($this->column) ? current($this->column) : $this->column;
295
296
        $label = isset($arguments[0]) ? $arguments[0] : ucfirst($column);
297
298
        return str_replace(['.', '_'], ' ', $label);
299
    }
300
301
    /**
302
     * Format the name of the field.
303
     *
304
     * @param string $column
305
     *
306
     * @return array|mixed|string
307
     */
308
    protected function formatName($column)
309
    {
310
        if (is_string($column)) {
311
            $name = explode('.', $column);
312
313
            if (count($name) == 1) {
314
                return $name[0];
315
            }
316
317
            $html = array_shift($name);
318
            foreach ($name as $piece) {
319
                $html .= "[$piece]";
320
            }
321
322
            return $html;
323
        }
324
325
        if (is_array($this->column)) {
326
            $names = [];
327
            foreach ($this->column as $key => $name) {
328
                $names[$key] = $this->formatName($name);
329
            }
330
331
            return $names;
332
        }
333
334
        return '';
335
    }
336
337
    /**
338
     * Set form element name.
339
     *
340
     * @param string $name
341
     *
342
     * @return $this
343
     *
344
     * @author Edwin Hui
345
     */
346
    public function setElementName($name)
347
    {
348
        $this->elementName = $name;
349
350
        return $this;
351
    }
352
353
    /**
354
     * Fill data to the field.
355
     *
356
     * @param array $data
357
     *
358
     * @return void
359
     */
360
    public function fill($data)
361
    {
362
        // Field value is already setted.
363
//        if (!is_null($this->value)) {
364
//            return;
365
//        }
366
367
        $this->data = $data;
368
369
        if (is_array($this->column)) {
370
            foreach ($this->column as $key => $column) {
371
                $this->value[$key] = Arr::get($data, $column);
372
            }
373
374
            return;
375
        }
376
377
        $this->value = Arr::get($data, $this->column);
378
        if (isset($this->customFormat) && $this->customFormat instanceof \Closure) {
379
            $this->value = call_user_func($this->customFormat, $this->value);
380
        }
381
    }
382
383
    /**
384
     * custom format form column data when edit.
385
     *
386
     * @param \Closure $call
387
     *
388
     * @return $this
389
     */
390
    public function customFormat(\Closure $call)
391
    {
392
        $this->customFormat = $call;
393
394
        return $this;
395
    }
396
397
    /**
398
     * Set original value to the field.
399
     *
400
     * @param array $data
401
     *
402
     * @return void
403
     */
404
    public function setOriginal($data)
405
    {
406
        if (is_array($this->column)) {
407
            foreach ($this->column as $key => $column) {
408
                $this->original[$key] = Arr::get($data, $column);
409
            }
410
411
            return;
412
        }
413
414
        $this->original = Arr::get($data, $this->column);
415
    }
416
417
    /**
418
     * @param Form $form
419
     *
420
     * @return $this
421
     */
422
    public function setForm(Form $form = null)
423
    {
424
        $this->form = $form;
425
426
        return $this;
427
    }
428
429
    /**
430
     * Set width for field and label.
431
     *
432
     * @param int $field
433
     * @param int $label
434
     *
435
     * @return $this
436
     */
437
    public function setWidth($field = 8, $label = 2)
438
    {
439
        $this->width = [
440
            'label' => $label,
441
            'field' => $field,
442
        ];
443
444
        return $this;
445
    }
446
447
    /**
448
     * Set the field options.
449
     *
450
     * @param array $options
451
     *
452
     * @return $this
453
     */
454 View Code Duplication
    public function options($options = [])
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...
455
    {
456
        if ($options instanceof Arrayable) {
457
            $options = $options->toArray();
458
        }
459
460
        $this->options = array_merge($this->options, $options);
461
462
        return $this;
463
    }
464
465
    /**
466
     * Set the field option checked.
467
     *
468
     * @param array $checked
469
     *
470
     * @return $this
471
     */
472 View Code Duplication
    public function checked($checked = [])
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...
473
    {
474
        if ($checked instanceof Arrayable) {
475
            $checked = $checked->toArray();
476
        }
477
478
        $this->checked = array_merge($this->checked, $checked);
479
480
        return $this;
481
    }
482
483
    /**
484
     * Add `required` attribute to current field if has required rule,
485
     * except file and image fields.
486
     *
487
     * @param array $rules
488
     */
489
    protected function addRequiredAttribute($rules)
490
    {
491
        if (!is_array($rules)) {
492
            return;
493
        }
494
495
        if (!in_array('required', $rules)) {
496
            return;
497
        }
498
499
        if ($this instanceof Form\Field\MultipleFile
500
            || $this instanceof Form\Field\File) {
501
            return;
502
        }
503
504
        $this->required();
505
    }
506
507
    /**
508
     * If has `required` rule, add required attribute to this field.
509
     */
510
    protected function addRequiredAttributeFromRules()
511
    {
512
        if (is_null($this->data)) {
513
            // Create page
514
            $this->addRequiredAttribute($this->creationRules ?? $this->rules);
0 ignored issues
show
Bug introduced by
It seems like $this->creationRules ?? $this->rules can also be of type object<Closure>; however, Encore\Admin\Form\Field::addRequiredAttribute() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
515
        } else {
516
            // Update page
517
            $this->addRequiredAttribute($this->updateRules ?? $this->rules);
0 ignored issues
show
Bug introduced by
It seems like $this->updateRules ?? $this->rules can also be of type object<Closure>; however, Encore\Admin\Form\Field::addRequiredAttribute() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
518
        }
519
    }
520
521
    /**
522
     * Set the validation rules for the field.
523
     *
524
     * @param array|callable|string  $rules
525
     * @param array $messages
526
     *
527
     * @return $this
528
     */
529 View Code Duplication
    public function rules($rules = null, $messages = [])
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...
530
    {
531
        if (is_string($rules)) {
532
            $this->rules = array_filter(explode('|', $rules));
533
        }
534
535
        $this->setValidationMessages('default', $messages);
536
537
        return $this;
538
    }
539
540
    /**
541
     * Set the update validation rules for the field.
542
     *
543
     * @param array|callable|string $rules
544
     * @param array $messages
545
     *
546
     * @return $this
547
     */
548 View Code Duplication
    public function updateRules($rules = null, $messages = [])
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...
549
    {
550
        if (is_string($rules)) {
551
            $this->updateRules = array_filter(explode('|', $rules));
552
        }
553
554
        $this->setValidationMessages('update', $messages);
555
556
        return $this;
557
    }
558
559
    /**
560
     * Set the creation validation rules for the field.
561
     *
562
     * @param array|callable|string $rules
563
     * @param array $messages
564
     * @return $this
565
     */
566 View Code Duplication
    public function creationRules($rules = null, $messages = [])
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...
567
    {
568
        if (is_string($rules)) {
569
            $this->creationRules = array_filter(explode('|', $rules));
570
        }
571
572
        $this->setValidationMessages('creation', $messages);
573
574
        return $this;
575
    }
576
577
    /**
578
     * Set validation messages for column.
579
     *
580
     * @param string $key
581
     * @param array $messages
582
     *
583
     * @return $this
584
     */
585
    public function setValidationMessages($key, array $messages)
586
    {
587
        $this->validationMessages[$key] = $messages;
588
        
589
        return $this;
590
    }
591
592
    /**
593
     * Get validation messages for the field.
594
     *
595
     * @return array|mixed
596
     */
597
    public function getValidationMessages()
598
    {
599
        // Default validation message.
600
        $messages = $this->validationMessages['default'] ?? [];
601
602
        if (request()->isMethod('POST')) {
603
            $messages = $this->validationMessages['creation'] ?? $messages;
604
        } elseif (request()->isMethod('PUT')) {
605
            $messages = $this->validationMessages['update'] ?? $messages;
606
        }
607
608
        return $messages;
609
    }
610
611
    /**
612
     * Get field validation rules.
613
     *
614
     * @return string
615
     */
616
    protected function getRules()
617
    {
618
        if (request()->isMethod('POST')) {
619
            $rules = $this->creationRules ?: $this->rules;
620
        } elseif (request()->isMethod('PUT')) {
621
            $rules = $this->updateRules ?: $this->rules;
622
        } else {
623
            $rules = $this->rules;
624
        }
625
626
        if ($rules instanceof \Closure) {
627
            return $rules->call($this, $this->form);
628
        }
629
630
        return $rules;
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $rules; (array) is incompatible with the return type documented by Encore\Admin\Form\Field::getRules of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
631
    }
632
633
    /**
634
     * Remove a specific rule by keyword.
635
     *
636
     * @param string $rule
637
     *
638
     * @return void
639
     */
640
    protected function removeRule($rule)
641
    {
642
        if (!is_string($this->rules)) {
643
            return;
644
        }
645
646
        $pattern = "/{$rule}[^\|]?(\||$)/";
647
        $this->rules = preg_replace($pattern, '', $this->rules, -1);
0 ignored issues
show
Documentation Bug introduced by
It seems like preg_replace($pattern, '', $this->rules, -1) of type string is incompatible with the declared type array|object<Closure> of property $rules.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
648
    }
649
650
    /**
651
     * Set field validator.
652
     *
653
     * @param callable $validator
654
     *
655
     * @return $this
656
     */
657
    public function validator(callable $validator)
658
    {
659
        $this->validator = $validator;
0 ignored issues
show
Documentation Bug introduced by
It seems like $validator of type callable is incompatible with the declared type object<Closure> of property $validator.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
660
661
        return $this;
662
    }
663
664
    /**
665
     * Get key for error message.
666
     *
667
     * @return string
668
     */
669
    public function getErrorKey()
670
    {
671
        return $this->errorKey ?: $this->column;
672
    }
673
674
    /**
675
     * Set key for error message.
676
     *
677
     * @param string $key
678
     *
679
     * @return $this
680
     */
681
    public function setErrorKey($key)
682
    {
683
        $this->errorKey = $key;
684
685
        return $this;
686
    }
687
688
    /**
689
     * Set or get value of the field.
690
     *
691
     * @param null $value
692
     *
693
     * @return mixed
694
     */
695 View Code Duplication
    public function value($value = 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...
696
    {
697
        if (is_null($value)) {
698
            return is_null($this->value) ? $this->getDefault() : $this->value;
699
        }
700
701
        $this->value = $value;
702
703
        return $this;
704
    }
705
706
    /**
707
     * Set or get data.
708
     *
709
     * @param array $data
710
     *
711
     * @return $this
712
     */
713
    public function data(array $data = null)
714
    {
715
        if (is_null($data)) {
716
            return $this->data;
717
        }
718
719
        $this->data = $data;
720
721
        return $this;
722
    }
723
724
    /**
725
     * Set default value for field.
726
     *
727
     * @param $default
728
     *
729
     * @return $this
730
     */
731
    public function default($default)
732
    {
733
        $this->default = $default;
734
735
        return $this;
736
    }
737
738
    /**
739
     * Get default value.
740
     *
741
     * @return mixed
742
     */
743
    public function getDefault()
744
    {
745
        if ($this->default instanceof \Closure) {
746
            return call_user_func($this->default, $this->form);
747
        }
748
749
        return $this->default;
750
    }
751
752
    /**
753
     * Set help block for current field.
754
     *
755
     * @param string $text
756
     * @param string $icon
757
     *
758
     * @return $this
759
     */
760
    public function help($text = '', $icon = 'fa-info-circle')
761
    {
762
        $this->help = compact('text', 'icon');
763
764
        return $this;
765
    }
766
767
    /**
768
     * Get column of the field.
769
     *
770
     * @return string|array
771
     */
772
    public function column()
773
    {
774
        return $this->column;
775
    }
776
777
    /**
778
     * Get label of the field.
779
     *
780
     * @return string
781
     */
782
    public function label()
783
    {
784
        return $this->label;
785
    }
786
787
    /**
788
     * Get original value of the field.
789
     *
790
     * @return mixed
791
     */
792
    public function original()
793
    {
794
        return $this->original;
795
    }
796
797
    /**
798
     * Get validator for this field.
799
     *
800
     * @param array $input
801
     *
802
     * @return bool|\Illuminate\Contracts\Validation\Validator|mixed
803
     */
804
    public function getValidator(array $input)
805
    {
806
        if ($this->validator) {
807
            return $this->validator->call($this, $input);
808
        }
809
810
        $rules = $attributes = [];
811
812
        if (!$fieldRules = $this->getRules()) {
813
            return false;
814
        }
815
816
        if (is_string($this->column)) {
817
            if (!Arr::has($input, $this->column)) {
818
                return false;
819
            }
820
821
            $input = $this->sanitizeInput($input, $this->column);
822
823
            $rules[$this->column] = $fieldRules;
824
            $attributes[$this->column] = $this->label;
825
        }
826
827
        if (is_array($this->column)) {
828
            foreach ($this->column as $key => $column) {
829
                if (!array_key_exists($column, $input)) {
830
                    continue;
831
                }
832
                $input[$column.$key] = Arr::get($input, $column);
833
                $rules[$column.$key] = $fieldRules;
834
                $attributes[$column.$key] = $this->label."[$column]";
835
            }
836
        }
837
838
        return \validator($input, $rules, $this->getValidationMessages(), $attributes);
839
    }
840
841
    /**
842
     * Sanitize input data.
843
     *
844
     * @param array  $input
845
     * @param string $column
846
     *
847
     * @return array
848
     */
849
    protected function sanitizeInput($input, $column)
850
    {
851
        if ($this instanceof Field\MultipleSelect) {
852
            $value = Arr::get($input, $column);
853
            Arr::set($input, $column, array_filter($value));
854
        }
855
856
        return $input;
857
    }
858
859
    /**
860
     * Add html attributes to elements.
861
     *
862
     * @param array|string $attribute
863
     * @param mixed        $value
864
     *
865
     * @return $this
866
     */
867
    public function attribute($attribute, $value = null)
868
    {
869
        if (is_array($attribute)) {
870
            $this->attributes = array_merge($this->attributes, $attribute);
871
        } else {
872
            $this->attributes[$attribute] = (string) $value;
873
        }
874
875
        return $this;
876
    }
877
878
    /**
879
     * Specifies a regular expression against which to validate the value of the input.
880
     *
881
     * @param string $regexp
882
     *
883
     * @return Field
884
     */
885
    public function pattern($regexp)
886
    {
887
        return $this->attribute('pattern', $regexp);
888
    }
889
890
    /**
891
     * set the input filed required.
892
     *
893
     * @param bool $isLabelAsterisked
894
     *
895
     * @return Field
896
     */
897
    public function required($isLabelAsterisked = true)
898
    {
899
        if ($isLabelAsterisked) {
900
            $this->setLabelClass(['asterisk']);
901
        }
902
903
        return $this->attribute('required', true);
904
    }
905
906
    /**
907
     * Set the field automatically get focus.
908
     *
909
     * @return Field
910
     */
911
    public function autofocus()
912
    {
913
        return $this->attribute('autofocus', true);
914
    }
915
916
    /**
917
     * Set the field as readonly mode.
918
     *
919
     * @return Field
920
     */
921
    public function readOnly()
922
    {
923
        return $this->attribute('readonly', true);
924
    }
925
926
    /**
927
     * Set field as disabled.
928
     *
929
     * @return Field
930
     */
931
    public function disable()
932
    {
933
        return $this->attribute('disabled', true);
934
    }
935
936
    /**
937
     * Set field placeholder.
938
     *
939
     * @param string $placeholder
940
     *
941
     * @return Field
942
     */
943
    public function placeholder($placeholder = '')
944
    {
945
        $this->placeholder = $placeholder;
946
947
        return $this;
948
    }
949
950
    /**
951
     * Get placeholder.
952
     *
953
     * @return string
954
     */
955
    public function getPlaceholder()
956
    {
957
        return $this->placeholder ?: trans('admin.input').' '.$this->label;
958
    }
959
960
    /**
961
     * Prepare for a field value before update or insert.
962
     *
963
     * @param $value
964
     *
965
     * @return mixed
966
     */
967
    public function prepare($value)
968
    {
969
        return $value;
970
    }
971
972
    /**
973
     * Format the field attributes.
974
     *
975
     * @return string
976
     */
977
    protected function formatAttributes()
978
    {
979
        $html = [];
980
981
        foreach ($this->attributes as $name => $value) {
982
            $html[] = $name.'="'.e($value).'"';
983
        }
984
985
        return implode(' ', $html);
986
    }
987
988
    /**
989
     * @return $this
990
     */
991
    public function disableHorizontal()
992
    {
993
        $this->horizontal = false;
994
995
        return $this;
996
    }
997
998
    /**
999
     * @return array
1000
     */
1001
    public function getViewElementClasses()
1002
    {
1003
        if ($this->horizontal) {
1004
            return [
1005
                'label'      => "col-sm-{$this->width['label']} {$this->getLabelClass()}",
1006
                'field'      => "col-sm-{$this->width['field']}",
1007
                'form-group' => 'form-group ',
1008
            ];
1009
        }
1010
1011
        return ['label' => "{$this->getLabelClass()}", 'field' => '', 'form-group' => ''];
1012
    }
1013
1014
    /**
1015
     * Set form element class.
1016
     *
1017
     * @param string|array $class
1018
     *
1019
     * @return $this
1020
     */
1021
    public function setElementClass($class)
1022
    {
1023
        $this->elementClass = array_merge($this->elementClass, (array) $class);
1024
1025
        return $this;
1026
    }
1027
1028
    /**
1029
     * Get element class.
1030
     *
1031
     * @return array
1032
     */
1033
    protected function getElementClass()
1034
    {
1035
        if (!$this->elementClass) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->elementClass of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1036
            $name = $this->elementName ?: $this->formatName($this->column);
0 ignored issues
show
Bug introduced by
It seems like $this->column can also be of type array; however, Encore\Admin\Form\Field::formatName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1037
1038
            $this->elementClass = (array) str_replace(['[', ']'], '_', $name);
1039
        }
1040
1041
        return $this->elementClass;
1042
    }
1043
1044
    /**
1045
     * Get element class string.
1046
     *
1047
     * @return mixed
1048
     */
1049 View Code Duplication
    protected function getElementClassString()
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...
1050
    {
1051
        $elementClass = $this->getElementClass();
1052
1053
        if (Arr::isAssoc($elementClass)) {
1054
            $classes = [];
1055
1056
            foreach ($elementClass as $index => $class) {
1057
                $classes[$index] = is_array($class) ? implode(' ', $class) : $class;
1058
            }
1059
1060
            return $classes;
1061
        }
1062
1063
        return implode(' ', $elementClass);
1064
    }
1065
1066
    /**
1067
     * Get element class selector.
1068
     *
1069
     * @return string|array
1070
     */
1071 View Code Duplication
    protected function getElementClassSelector()
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...
1072
    {
1073
        $elementClass = $this->getElementClass();
1074
1075
        if (Arr::isAssoc($elementClass)) {
1076
            $classes = [];
1077
1078
            foreach ($elementClass as $index => $class) {
1079
                $classes[$index] = '.'.(is_array($class) ? implode('.', $class) : $class);
1080
            }
1081
1082
            return $classes;
1083
        }
1084
1085
        return '.'.implode('.', $elementClass);
1086
    }
1087
1088
    /**
1089
     * Add the element class.
1090
     *
1091
     * @param $class
1092
     *
1093
     * @return $this
1094
     */
1095
    public function addElementClass($class)
1096
    {
1097
        if (is_array($class) || is_string($class)) {
1098
            $this->elementClass = array_merge($this->elementClass, (array) $class);
1099
1100
            $this->elementClass = array_unique($this->elementClass);
1101
        }
1102
1103
        return $this;
1104
    }
1105
1106
    /**
1107
     * Remove element class.
1108
     *
1109
     * @param $class
1110
     *
1111
     * @return $this
1112
     */
1113
    public function removeElementClass($class)
1114
    {
1115
        $delClass = [];
1116
1117
        if (is_string($class) || is_array($class)) {
1118
            $delClass = (array) $class;
1119
        }
1120
1121
        foreach ($delClass as $del) {
1122
            if (($key = array_search($del, $this->elementClass)) !== false) {
1123
                unset($this->elementClass[$key]);
1124
            }
1125
        }
1126
1127
        return $this;
1128
    }
1129
1130
    /**
1131
     * Set form group class.
1132
     *
1133
     * @param string|array $class
1134
     *
1135
     * @return $this
1136
     */
1137
    public function setGroupClass($class)
1138
    : self
1139
    {
1140
        array_push($this->groupClass, $class);
1141
1142
        return $this;
1143
    }
1144
1145
    /**
1146
     * Get element class.
1147
     *
1148
     * @param bool $default
1149
     *
1150
     * @return string
1151
     */
1152
    protected function getGroupClass($default = false)
1153
    : string
1154
    {
1155
        return ($default ? 'form-group ' : '').implode(' ', array_filter($this->groupClass));
1156
    }
1157
1158
    /**
1159
     * reset field className.
1160
     *
1161
     * @param string $className
1162
     * @param string $resetClassName
1163
     *
1164
     * @return $this
1165
     */
1166
    public function resetElementClassName(string $className, string $resetClassName)
1167
    {
1168
        if (($key = array_search($className, $this->getElementClass())) !== false) {
1169
            $this->elementClass[$key] = $resetClassName;
1170
        }
1171
1172
        return $this;
1173
    }
1174
1175
    /**
1176
     * Add variables to field view.
1177
     *
1178
     * @param array $variables
1179
     *
1180
     * @return $this
1181
     */
1182
    protected function addVariables(array $variables = [])
1183
    {
1184
        $this->variables = array_merge($this->variables, $variables);
1185
1186
        return $this;
1187
    }
1188
1189
    /**
1190
     * @return string
1191
     */
1192
    public function getLabelClass()
1193
    : string
1194
    {
1195
        return implode(' ', $this->labelClass);
1196
    }
1197
1198
    /**
1199
     * @param array $labelClass
1200
     *
1201
     * @return self
1202
     */
1203
    public function setLabelClass(array $labelClass)
1204
    : self
1205
    {
1206
        $this->labelClass = $labelClass;
1207
1208
        return $this;
1209
    }
1210
1211
    /**
1212
     * Get the view variables of this field.
1213
     *
1214
     * @return array
1215
     */
1216
    public function variables()
1217
    {
1218
        return array_merge($this->variables, [
1219
            'id'          => $this->id,
1220
            'name'        => $this->elementName ?: $this->formatName($this->column),
0 ignored issues
show
Bug introduced by
It seems like $this->column can also be of type array; however, Encore\Admin\Form\Field::formatName() does only seem to accept string, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
1221
            'help'        => $this->help,
1222
            'class'       => $this->getElementClassString(),
1223
            'value'       => $this->value(),
1224
            'label'       => $this->label,
1225
            'viewClass'   => $this->getViewElementClasses(),
1226
            'column'      => $this->column,
1227
            'errorKey'    => $this->getErrorKey(),
1228
            'attributes'  => $this->formatAttributes(),
1229
            'placeholder' => $this->getPlaceholder(),
1230
        ]);
1231
    }
1232
1233
    /**
1234
     * Get view of this field.
1235
     *
1236
     * @return string
1237
     */
1238
    public function getView()
1239
    {
1240
        if (!empty($this->view)) {
1241
            return $this->view;
1242
        }
1243
1244
        $class = explode('\\', get_called_class());
1245
1246
        return 'admin::form.'.strtolower(end($class));
1247
    }
1248
1249
    /**
1250
     * Set view of current field.
1251
     *
1252
     * @param string $view
1253
     *
1254
     * @return string
1255
     */
1256
    public function setView($view)
1257
    {
1258
        $this->view = $view;
1259
1260
        return $this;
1261
    }
1262
1263
    /**
1264
     * Get script of current field.
1265
     *
1266
     * @return string
1267
     */
1268
    public function getScript()
1269
    {
1270
        return $this->script;
1271
    }
1272
1273
    /**
1274
     * Set script of current field.
1275
     *
1276
     * @param string $script
1277
     *
1278
     * @return $this
1279
     */
1280
    public function setScript($script)
1281
    {
1282
        $this->script = $script;
1283
1284
        return $this;
1285
    }
1286
1287
    /**
1288
     * To set this field should render or not.
1289
     *
1290
     * @param bool $display
1291
     *
1292
     * @return $this
1293
     */
1294
    public function setDisplay(bool $display)
1295
    {
1296
        $this->display = $display;
1297
1298
        return $this;
1299
    }
1300
1301
    /**
1302
     * If this field should render.
1303
     *
1304
     * @return bool
1305
     */
1306
    protected function shouldRender()
1307
    {
1308
        if (!$this->display) {
1309
            return false;
1310
        }
1311
1312
        return true;
1313
    }
1314
1315
    /**
1316
     * @param \Closure $callback
1317
     *
1318
     * @return \Encore\Admin\Form\Field
1319
     */
1320
    public function with(Closure $callback)
1321
    {
1322
        $this->callback = $callback;
1323
1324
        return $this;
1325
    }
1326
1327
    /**
1328
     * Render this filed.
1329
     *
1330
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View|string
1331
     */
1332
    public function render()
1333
    {
1334
        if (!$this->shouldRender()) {
1335
            return '';
1336
        }
1337
1338
        if ($this->callback instanceof Closure) {
1339
            $this->value = $this->callback->call($this->form->model(), $this->value, $this);
1340
        }
1341
1342
        $this->addRequiredAttributeFromRules();
1343
1344
        Admin::script($this->script);
1345
1346
        return view($this->getView(), $this->variables());
0 ignored issues
show
Bug Compatibility introduced by
The expression view($this->getView(), $this->variables()); of type Illuminate\View\View|Ill...\Contracts\View\Factory adds the type Illuminate\Contracts\View\Factory to the return on line 1346 which is incompatible with the return type declared by the interface Illuminate\Contracts\Support\Renderable::render of type string.
Loading history...
1347
    }
1348
1349
    /**
1350
     * @return string
1351
     */
1352
    public function __toString()
1353
    {
1354
        return $this->render()->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...
1355
    }
1356
}
1357