FormField::Value()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms;
4
5
use LogicException;
6
use ReflectionClass;
7
use SilverStripe\Control\Controller;
8
use SilverStripe\Control\RequestHandler;
9
use SilverStripe\Core\ClassInfo;
10
use SilverStripe\Core\Convert;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\ORM\DataObjectInterface;
13
use SilverStripe\ORM\FieldType\DBField;
14
use SilverStripe\ORM\FieldType\DBHTMLText;
15
use SilverStripe\ORM\ValidationResult;
16
use SilverStripe\View\SSViewer;
17
18
/**
19
 * Represents a field in a form.
20
 *
21
 * A FieldList contains a number of FormField objects which make up the whole of a form.
22
 *
23
 * In addition to single fields, FormField objects can be "composite", for example, the
24
 * {@link TabSet} field. Composite fields let us define complex forms without having to resort to
25
 * custom HTML.
26
 *
27
 * To subclass:
28
 *
29
 * Define a {@link dataValue()} method that returns a value suitable for inserting into a single
30
 * database field.
31
 *
32
 * For example, you might tidy up the format of a date or currency field. Define {@link saveInto()}
33
 * to totally customise saving.
34
 *
35
 * For example, data might be saved to the filesystem instead of the data record, or saved to a
36
 * component of the data record instead of the data record itself.
37
 *
38
 * A form field can be represented as structured data through {@link FormSchema},
39
 * including both structure (name, id, attributes, etc.) and state (field value).
40
 * Can be used by for JSON data which is consumed by a front-end application.
41
 */
42
class FormField extends RequestHandler
43
{
44
    use FormMessage;
45
46
    /** @see $schemaDataType */
47
    const SCHEMA_DATA_TYPE_STRING = 'String';
48
49
    /** @see $schemaDataType */
50
    const SCHEMA_DATA_TYPE_HIDDEN = 'Hidden';
51
52
    /** @see $schemaDataType */
53
    const SCHEMA_DATA_TYPE_TEXT = 'Text';
54
55
    /** @see $schemaDataType */
56
    const SCHEMA_DATA_TYPE_HTML = 'HTML';
57
58
    /** @see $schemaDataType */
59
    const SCHEMA_DATA_TYPE_INTEGER = 'Integer';
60
61
    /** @see $schemaDataType */
62
    const SCHEMA_DATA_TYPE_DECIMAL = 'Decimal';
63
64
    /** @see $schemaDataType */
65
    const SCHEMA_DATA_TYPE_MULTISELECT = 'MultiSelect';
66
67
    /** @see $schemaDataType */
68
    const SCHEMA_DATA_TYPE_SINGLESELECT = 'SingleSelect';
69
70
    /** @see $schemaDataType */
71
    const SCHEMA_DATA_TYPE_DATE = 'Date';
72
73
    /** @see $schemaDataType */
74
    const SCHEMA_DATA_TYPE_DATETIME = 'Datetime';
75
76
    /** @see $schemaDataType */
77
    const SCHEMA_DATA_TYPE_TIME = 'Time';
78
79
    /** @see $schemaDataType */
80
    const SCHEMA_DATA_TYPE_BOOLEAN = 'Boolean';
81
82
    /** @see $schemaDataType */
83
    const SCHEMA_DATA_TYPE_CUSTOM = 'Custom';
84
85
    /** @see $schemaDataType */
86
    const SCHEMA_DATA_TYPE_STRUCTURAL = 'Structural';
87
88
    /**
89
     * @var Form
90
     */
91
    protected $form;
92
93
    /**
94
     * This is INPUT's type attribute value.
95
     *
96
     * @var string
97
     */
98
    protected $inputType = 'text';
99
100
    /**
101
     * @var string
102
     */
103
    protected $name;
104
105
    /**
106
     * @var null|string
107
     */
108
    protected $title;
109
110
    /**
111
     * @var mixed
112
     */
113
    protected $value;
114
115
    /**
116
     * @var string
117
     */
118
    protected $extraClass;
119
120
    /**
121
     * Adds a title attribute to the markup.
122
     *
123
     * @var string
124
     *
125
     * @todo Implement in all subclasses
126
     */
127
    protected $description;
128
129
    /**
130
     * Extra CSS classes for the FormField container.
131
     *
132
     * @var array
133
     */
134
    protected $extraClasses;
135
136
    /**
137
     * @config
138
     * @var array $default_classes The default classes to apply to the FormField
139
     */
140
    private static $default_classes = [];
0 ignored issues
show
introduced by
The private property $default_classes is not used, and could be removed.
Loading history...
141
142
    /**
143
     * Right-aligned, contextual label for the field.
144
     *
145
     * @var string
146
     */
147
    protected $rightTitle;
148
149
    /**
150
     * Left-aligned, contextual label for the field.
151
     *
152
     * @var string
153
     */
154
    protected $leftTitle;
155
156
    /**
157
     * Stores a reference to the FieldList that contains this object.
158
     *
159
     * @var FieldList
160
     */
161
    protected $containerFieldList;
162
163
    /**
164
     * @var bool
165
     */
166
    protected $readonly = false;
167
168
    /**
169
     * @var bool
170
     */
171
    protected $disabled = false;
172
173
    /**
174
     * @var bool
175
     */
176
    protected $autofocus = false;
177
178
    /**
179
     * Custom validation message for the field.
180
     *
181
     * @var string
182
     */
183
    protected $customValidationMessage = '';
184
185
    /**
186
     * Name of the template used to render this form field. If not set, then will look up the class
187
     * ancestry for the first matching template where the template name equals the class name.
188
     *
189
     * To explicitly use a custom template or one named other than the form field see
190
     * {@link setTemplate()}.
191
     *
192
     * @var string
193
     */
194
    protected $template;
195
196
    /**
197
     * Name of the template used to render this form field. If not set, then will look up the class
198
     * ancestry for the first matching template where the template name equals the class name.
199
     *
200
     * To explicitly use a custom template or one named other than the form field see
201
     * {@link setFieldHolderTemplate()}.
202
     *
203
     * @var string
204
     */
205
    protected $fieldHolderTemplate;
206
207
    /**
208
     * @var string
209
     */
210
    protected $smallFieldHolderTemplate;
211
212
    /**
213
     * All attributes on the form field (not the field holder).
214
     *
215
     * Partially determined based on other instance properties.
216
     *
217
     * @see getAttributes()
218
     *
219
     * @var array
220
     */
221
    protected $attributes = [];
222
223
    /**
224
     * The data type backing the field. Represents the type of value the
225
     * form expects to receive via a postback. Should be set in subclasses.
226
     *
227
     * The values allowed in this list include:
228
     *
229
     *   - String: Single line text
230
     *   - Hidden: Hidden field which is posted back without modification
231
     *   - Text: Multi line text
232
     *   - HTML: Rich html text
233
     *   - Integer: Whole number value
234
     *   - Decimal: Decimal value
235
     *   - MultiSelect: Select many from source
236
     *   - SingleSelect: Select one from source
237
     *   - Date: Date only
238
     *   - DateTime: Date and time
239
     *   - Time: Time only
240
     *   - Boolean: Yes or no
241
     *   - Custom: Custom type declared by the front-end component. For fields with this type,
242
     *     the component property is mandatory, and will determine the posted value for this field.
243
     *   - Structural: Represents a field that is NOT posted back. This may contain other fields,
244
     *     or simply be a block of stand-alone content. As with 'Custom',
245
     *     the component property is mandatory if this is assigned.
246
     *
247
     * Each value has an equivalent constant, e.g. {@link self::SCHEMA_DATA_TYPE_STRING}.
248
     *
249
     * @var string
250
     */
251
    protected $schemaDataType;
252
253
    /**
254
     * The type of front-end component to render the FormField as.
255
     *
256
     * @skipUpgrade
257
     * @var string
258
     */
259
    protected $schemaComponent;
260
261
    /**
262
     * Structured schema data representing the FormField.
263
     * Used to render the FormField as a ReactJS Component on the front-end.
264
     *
265
     * @var array
266
     */
267
    protected $schemaData = [];
268
269
    private static $casting = array(
0 ignored issues
show
introduced by
The private property $casting is not used, and could be removed.
Loading history...
270
        'FieldHolder' => 'HTMLFragment',
271
        'SmallFieldHolder' => 'HTMLFragment',
272
        'Field' => 'HTMLFragment',
273
        'AttributesHTML' => 'HTMLFragment', // property $AttributesHTML version
274
        'getAttributesHTML' => 'HTMLFragment', // method $getAttributesHTML($arg) version
275
        'Value' => 'Text',
276
        'extraClass' => 'Text',
277
        'ID' => 'Text',
278
        'isReadOnly' => 'Boolean',
279
        'HolderID' => 'Text',
280
        'Title' => 'Text',
281
        'RightTitle' => 'Text',
282
        'Description' => 'HTMLFragment',
283
    );
284
285
    /**
286
     * Structured schema state representing the FormField's current data and validation.
287
     * Used to render the FormField as a ReactJS Component on the front-end.
288
     *
289
     * @var array
290
     */
291
    protected $schemaState = [];
292
293
    /**
294
     * Takes a field name and converts camelcase to spaced words. Also resolves combined field
295
     * names with dot syntax to spaced words.
296
     *
297
     * Examples:
298
     *
299
     * - 'TotalAmount' will return 'Total amount'
300
     * - 'Organisation.ZipCode' will return 'Organisation zip code'
301
     *
302
     * @param string $fieldName
303
     *
304
     * @return string
305
     */
306
    public static function name_to_label($fieldName)
307
    {
308
        // Handle dot delimiters
309
        if (strpos($fieldName, '.') !== false) {
310
            $parts = explode('.', $fieldName);
311
            // Ensure that any letter following a dot is uppercased, so that the regex below can break it up
312
            // into words
313
            $label = implode(array_map('ucfirst', $parts));
314
        } else {
315
            $label = $fieldName;
316
        }
317
318
        // Replace any capital letter that is followed by a lowercase letter with a space, the lowercased
319
        // version of itself then the remaining lowercase letters.
320
        $labelWithSpaces = preg_replace_callback('/([A-Z])([a-z]+)/', function ($matches) {
321
            return ' ' . strtolower($matches[1]) . $matches[2];
322
        }, $label);
323
324
        // Add a space before any capital letter block that is at the end of the string
325
        $labelWithSpaces = preg_replace('/([a-z])([A-Z]+)$/', '$1 $2', $labelWithSpaces);
326
327
        // The first letter should be uppercase
328
        return ucfirst(trim($labelWithSpaces));
329
    }
330
331
    /**
332
     * Creates a new field.
333
     *
334
     * @param string $name The internal field name, passed to forms.
335
     * @param null|string|SilverStripe\View\ViewableData $title The human-readable field label.
0 ignored issues
show
Bug introduced by
The type SilverStripe\Forms\SilverStripe\View\ViewableData was not found. Did you mean SilverStripe\View\ViewableData? If so, make sure to prefix the type with \.
Loading history...
336
     * @param mixed $value The value of the field.
337
     */
338
    public function __construct($name, $title = null, $value = null)
339
    {
340
        $this->setName($name);
341
342
        if ($title === null) {
343
            $this->title = self::name_to_label($name);
344
        } else {
345
            $this->title = $title;
346
        }
347
348
        if ($value !== null) {
349
            $this->setValue($value);
350
        }
351
352
        parent::__construct();
353
354
        $this->setupDefaultClasses();
355
    }
356
357
    /**
358
     * Set up the default classes for the form. This is done on construct so that the default classes can be removed
359
     * after instantiation
360
     */
361
    protected function setupDefaultClasses()
362
    {
363
        $defaultClasses = $this->config()->get('default_classes');
364
        if ($defaultClasses) {
365
            foreach ($defaultClasses as $class) {
366
                $this->addExtraClass($class);
367
            }
368
        }
369
    }
370
371
    /**
372
     * Return a link to this field
373
     *
374
     * @param string $action
375
     * @return string
376
     * @throws LogicException If no form is set yet
377
     */
378
    public function Link($action = null)
379
    {
380
        if (!$this->form) {
381
            throw new LogicException(
382
                'Field must be associated with a form to call Link(). Please use $field->setForm($form);'
383
            );
384
        }
385
386
        $link = Controller::join_links($this->form->FormAction(), 'field/' . $this->name, $action);
387
        $this->extend('updateLink', $link, $action);
388
        return $link;
389
    }
390
391
    /**
392
     * Returns the HTML ID of the field.
393
     *
394
     * The ID is generated as FormName_FieldName. All Field functions should ensure that this ID is
395
     * included in the field.
396
     *
397
     * @return string
398
     */
399
    public function ID()
400
    {
401
        return $this->getTemplateHelper()->generateFieldID($this);
402
    }
403
404
    /**
405
     * Returns the HTML ID for the form field holder element.
406
     *
407
     * @return string
408
     */
409
    public function HolderID()
410
    {
411
        return $this->getTemplateHelper()->generateFieldHolderID($this);
412
    }
413
414
    /**
415
     * Returns the current {@link FormTemplateHelper} on either the parent
416
     * Form or the global helper set through the {@link Injector} layout.
417
     *
418
     * To customize a single {@link FormField}, use {@link setTemplate} and
419
     * provide a custom template name.
420
     *
421
     * @return FormTemplateHelper
422
     */
423
    public function getTemplateHelper()
424
    {
425
        if ($this->form) {
426
            return $this->form->getTemplateHelper();
427
        }
428
429
        return FormTemplateHelper::singleton();
430
    }
431
432
    /**
433
     * Returns the field name.
434
     *
435
     * @return string
436
     */
437
    public function getName()
438
    {
439
        return $this->name;
440
    }
441
442
    /**
443
     * Returns the field input name.
444
     *
445
     * @return string
446
     */
447
    public function getInputType()
448
    {
449
        return $this->inputType;
450
    }
451
452
    /**
453
     * Returns the field value.
454
     *
455
     * @see FormField::setSubmittedValue()
456
     * @return mixed
457
     */
458
    public function Value()
459
    {
460
        return $this->value;
461
    }
462
463
    /**
464
     * Method to save this form field into the given {@link DataObject}.
465
     *
466
     * By default, makes use of $this->dataValue()
467
     *
468
     * @param DataObject|DataObjectInterface $record DataObject to save data into
469
     */
470
    public function saveInto(DataObjectInterface $record)
471
    {
472
        if ($this->name) {
473
            $record->setCastedField($this->name, $this->dataValue());
474
        }
475
    }
476
477
    /**
478
     * Returns the field value suitable for insertion into the data object.
479
     * @see Formfield::setValue()
480
     * @return mixed
481
     */
482
    public function dataValue()
483
    {
484
        return $this->value;
485
    }
486
487
    /**
488
     * Returns the field label - used by templates.
489
     *
490
     * @return string
491
     */
492
    public function Title()
493
    {
494
        return $this->title;
495
    }
496
497
    /**
498
     * Set the title of this formfield.
499
     * Note: This expects escaped HTML.
500
     *
501
     * @param string $title Escaped HTML for title
502
     * @return $this
503
     */
504
    public function setTitle($title)
505
    {
506
        $this->title = $title;
507
        return $this;
508
    }
509
510
    /**
511
     * Gets the contextual label than can be used for additional field description.
512
     * Can be shown to the right or under the field in question.
513
     *
514
     * @return string Contextual label text
515
     */
516
    public function RightTitle()
517
    {
518
        return $this->rightTitle;
519
    }
520
521
    /**
522
     * Sets the right title for this formfield
523
     *
524
     * @param string|DBField Plain text string, or a DBField with appropriately escaped HTML
0 ignored issues
show
Bug introduced by
The type SilverStripe\Forms\Plain was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
525
     * @return $this
526
     */
527
    public function setRightTitle($rightTitle)
528
    {
529
        $this->rightTitle = $rightTitle;
530
        return $this;
531
    }
532
533
    /**
534
     * @return string
535
     */
536
    public function LeftTitle()
537
    {
538
        return $this->leftTitle;
539
    }
540
541
    /**
542
     * @param string $leftTitle
543
     *
544
     * @return $this
545
     */
546
    public function setLeftTitle($leftTitle)
547
    {
548
        $this->leftTitle = $leftTitle;
549
550
        return $this;
551
    }
552
553
    /**
554
     * Compiles all CSS-classes. Optionally includes a "form-group--no-label" class if no title was set on the
555
     * FormField.
556
     *
557
     * Uses {@link Message()} and {@link MessageType()} to add validation error classes which can
558
     * be used to style the contained tags.
559
     *
560
     * @return string
561
     */
562
    public function extraClass()
563
    {
564
        $classes = array();
565
566
        $classes[] = $this->Type();
567
568
        if ($this->extraClasses) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->extraClasses 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...
569
            $classes = array_merge(
570
                $classes,
571
                array_values($this->extraClasses)
572
            );
573
        }
574
575
        if (!$this->Title()) {
576
            $classes[] = 'form-group--no-label';
577
        }
578
579
        // Allow custom styling of any element in the container based on validation errors,
580
        // e.g. red borders on input tags.
581
        //
582
        // CSS class needs to be different from the one rendered through {@link FieldHolder()}.
583
        if ($this->getMessage()) {
584
            $classes[] = 'holder-' . $this->getMessageType();
585
        }
586
587
        return implode(' ', $classes);
588
    }
589
590
    /**
591
     * Add one or more CSS-classes to the FormField container.
592
     *
593
     * Multiple class names should be space delimited.
594
     *
595
     * @param string $class
596
     *
597
     * @return $this
598
     */
599
    public function addExtraClass($class)
600
    {
601
        $classes = preg_split('/\s+/', $class);
602
603
        foreach ($classes as $class) {
0 ignored issues
show
introduced by
$class is overwriting one of the parameters of this function.
Loading history...
604
            $this->extraClasses[$class] = $class;
605
        }
606
607
        return $this;
608
    }
609
610
    /**
611
     * Remove one or more CSS-classes from the FormField container.
612
     *
613
     * @param string $class
614
     *
615
     * @return $this
616
     */
617
    public function removeExtraClass($class)
618
    {
619
        $classes = preg_split('/\s+/', $class);
620
621
        foreach ($classes as $class) {
0 ignored issues
show
introduced by
$class is overwriting one of the parameters of this function.
Loading history...
622
            unset($this->extraClasses[$class]);
623
        }
624
625
        return $this;
626
    }
627
628
    /**
629
     * Set an HTML attribute on the field element, mostly an input tag.
630
     *
631
     * Some attributes are best set through more specialized methods, to avoid interfering with
632
     * built-in behaviour:
633
     *
634
     * - 'class': {@link addExtraClass()}
635
     * - 'title': {@link setDescription()}
636
     * - 'value': {@link setValue}
637
     * - 'name': {@link setName}
638
     *
639
     * Caution: this doesn't work on most fields which are composed of more than one HTML form
640
     * field.
641
     *
642
     * @param string $name
643
     * @param string $value
644
     *
645
     * @return $this
646
     */
647
    public function setAttribute($name, $value)
648
    {
649
        $this->attributes[$name] = $value;
650
651
        return $this;
652
    }
653
654
    /**
655
     * Get an HTML attribute defined by the field, or added through {@link setAttribute()}.
656
     *
657
     * Caution: this doesn't work on all fields, see {@link setAttribute()}.
658
     *
659
     * @param string $name
660
     * @return string
661
     */
662
    public function getAttribute($name)
663
    {
664
        $attributes = $this->getAttributes();
665
666
        if (isset($attributes[$name])) {
667
            return $attributes[$name];
668
        }
669
670
        return null;
671
    }
672
673
    /**
674
     * Allows customization through an 'updateAttributes' hook on the base class.
675
     * Existing attributes are passed in as the first argument and can be manipulated,
676
     * but any attributes added through a subclass implementation won't be included.
677
     *
678
     * @return array
679
     */
680
    public function getAttributes()
681
    {
682
        $attributes = array(
683
            'type' => $this->getInputType(),
684
            'name' => $this->getName(),
685
            'value' => $this->Value(),
686
            'class' => $this->extraClass(),
687
            'id' => $this->ID(),
688
            'disabled' => $this->isDisabled(),
689
            'readonly' => $this->isReadonly(),
690
            'autofocus' => $this->isAutofocus()
691
        );
692
693
        if ($this->Required()) {
694
            $attributes['required'] = 'required';
695
            $attributes['aria-required'] = 'true';
696
        }
697
698
        $attributes = array_merge($attributes, $this->attributes);
699
700
        $this->extend('updateAttributes', $attributes);
701
702
        return $attributes;
703
    }
704
705
    /**
706
     * Custom attributes to process. Falls back to {@link getAttributes()}.
707
     *
708
     * If at least one argument is passed as a string, all arguments act as excludes, by name.
709
     *
710
     * @param array $attributes
711
     *
712
     * @return string
713
     */
714
    public function getAttributesHTML($attributes = null)
715
    {
716
        $exclude = null;
717
718
        if (is_string($attributes)) {
719
            $exclude = func_get_args();
720
        }
721
722
        if (!$attributes || is_string($attributes)) {
723
            $attributes = $this->getAttributes();
724
        }
725
726
        $attributes = (array) $attributes;
727
728
        $attributes = array_filter($attributes, function ($v) {
729
            return ($v || $v === 0 || $v === '0');
730
        });
731
732
        if ($exclude) {
733
            $attributes = array_diff_key(
734
                $attributes,
735
                array_flip($exclude)
736
            );
737
        }
738
739
        // Create markup
740
        $parts = array();
741
742
        foreach ($attributes as $name => $value) {
743
            if ($value === true) {
744
                $value = $name;
745
            } else {
746
                if (is_scalar($value)) {
747
                    $value = (string) $value;
748
                } else {
749
                    $value = json_encode($value);
750
                }
751
            }
752
753
            $parts[] = sprintf('%s="%s"', Convert::raw2att($name), Convert::raw2att($value));
0 ignored issues
show
Bug introduced by
It seems like SilverStripe\Core\Convert::raw2att($name) can also be of type array and array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

753
            $parts[] = sprintf('%s="%s"', /** @scrutinizer ignore-type */ Convert::raw2att($name), Convert::raw2att($value));
Loading history...
754
        }
755
756
        return implode(' ', $parts);
757
    }
758
759
    /**
760
     * Returns a version of a title suitable for insertion into an HTML attribute.
761
     *
762
     * @return string
763
     */
764
    public function attrTitle()
765
    {
766
        return Convert::raw2att($this->title);
767
    }
768
769
    /**
770
     * Returns a version of a title suitable for insertion into an HTML attribute.
771
     *
772
     * @return string
773
     */
774
    public function attrValue()
775
    {
776
        return Convert::raw2att($this->value);
777
    }
778
779
    /**
780
     * Set the field value.
781
     *
782
     * If a FormField requires specific behaviour for loading content from either the database
783
     * or a submitted form value they should override setSubmittedValue() instead.
784
     *
785
     * @param mixed $value Either the parent object, or array of source data being loaded
786
     * @param array|DataObject $data {@see Form::loadDataFrom}
787
     * @return $this
788
     */
789
    public function setValue($value, $data = null)
790
    {
791
        $this->value = $value;
792
        return $this;
793
    }
794
795
    /**
796
     * Set value assigned from a submitted form postback.
797
     * Can be overridden to handle custom behaviour for user-localised
798
     * data formats.
799
     *
800
     * @param mixed $value
801
     * @param array|DataObject $data
802
     * @return $this
803
     */
804
    public function setSubmittedValue($value, $data = null)
805
    {
806
        return $this->setValue($value, $data);
807
    }
808
809
    /**
810
     * Set the field name.
811
     *
812
     * @param string $name
813
     *
814
     * @return $this
815
     */
816
    public function setName($name)
817
    {
818
        $this->name = $name;
819
820
        return $this;
821
    }
822
823
    /**
824
     * Set the field input type.
825
     *
826
     * @param string $type
827
     *
828
     * @return $this
829
     */
830
    public function setInputType($type)
831
    {
832
        $this->inputType = $type;
833
834
        return $this;
835
    }
836
837
    /**
838
     * Set the container form.
839
     *
840
     * This is called automatically when fields are added to forms.
841
     *
842
     * @param Form $form
843
     *
844
     * @return $this
845
     */
846
    public function setForm($form)
847
    {
848
        $this->form = $form;
849
850
        return $this;
851
    }
852
853
    /**
854
     * Get the currently used form.
855
     *
856
     * @return Form
857
     */
858
    public function getForm()
859
    {
860
        return $this->form;
861
    }
862
863
    /**
864
     * Return true if security token protection is enabled on the parent {@link Form}.
865
     *
866
     * @return bool
867
     */
868
    public function securityTokenEnabled()
869
    {
870
        $form = $this->getForm();
871
872
        if (!$form) {
0 ignored issues
show
introduced by
$form is of type SilverStripe\Forms\Form, thus it always evaluated to true.
Loading history...
873
            return false;
874
        }
875
876
        return $form->getSecurityToken()->isEnabled();
877
    }
878
879
    public function castingHelper($field)
880
    {
881
        // Override casting for field message
882
        if (strcasecmp($field, 'Message') === 0 && ($helper = $this->getMessageCastingHelper())) {
883
            return $helper;
884
        }
885
        return parent::castingHelper($field);
886
    }
887
888
    /**
889
     * Set the custom error message to show instead of the default format.
890
     *
891
     * Different from setError() as that appends it to the standard error messaging.
892
     *
893
     * @param string $customValidationMessage
894
     *
895
     * @return $this
896
     */
897
    public function setCustomValidationMessage($customValidationMessage)
898
    {
899
        $this->customValidationMessage = $customValidationMessage;
900
901
        return $this;
902
    }
903
904
    /**
905
     * Get the custom error message for this form field. If a custom message has not been defined
906
     * then just return blank. The default error is defined on {@link Validator}.
907
     *
908
     * @return string
909
     */
910
    public function getCustomValidationMessage()
911
    {
912
        return $this->customValidationMessage;
913
    }
914
915
    /**
916
     * Set name of template (without path or extension).
917
     *
918
     * Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
919
     * method on the subclass for support.
920
     *
921
     * @param string $template
922
     *
923
     * @return $this
924
     */
925
    public function setTemplate($template)
926
    {
927
        $this->template = $template;
928
929
        return $this;
930
    }
931
932
    /**
933
     * @return string
934
     */
935
    public function getTemplate()
936
    {
937
        return $this->template;
938
    }
939
940
    /**
941
     * @return string
942
     */
943
    public function getFieldHolderTemplate()
944
    {
945
        return $this->fieldHolderTemplate;
946
    }
947
948
    /**
949
     * Set name of template (without path or extension) for the holder, which in turn is
950
     * responsible for rendering {@link Field()}.
951
     *
952
     * Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
953
     * method on the subclass for support.
954
     *
955
     * @param string $fieldHolderTemplate
956
     *
957
     * @return $this
958
     */
959
    public function setFieldHolderTemplate($fieldHolderTemplate)
960
    {
961
        $this->fieldHolderTemplate = $fieldHolderTemplate;
962
963
        return $this;
964
    }
965
966
    /**
967
     * @return string
968
     */
969
    public function getSmallFieldHolderTemplate()
970
    {
971
        return $this->smallFieldHolderTemplate;
972
    }
973
974
    /**
975
     * Set name of template (without path or extension) for the small holder, which in turn is
976
     * responsible for rendering {@link Field()}.
977
     *
978
     * Caution: Not consistently implemented in all subclasses, please check the {@link Field()}
979
     * method on the subclass for support.
980
     *
981
     * @param string $smallFieldHolderTemplate
982
     *
983
     * @return $this
984
     */
985
    public function setSmallFieldHolderTemplate($smallFieldHolderTemplate)
986
    {
987
        $this->smallFieldHolderTemplate = $smallFieldHolderTemplate;
988
989
        return $this;
990
    }
991
992
    /**
993
     * Returns the form field.
994
     *
995
     * Although FieldHolder is generally what is inserted into templates, all of the field holder
996
     * templates make use of $Field. It's expected that FieldHolder will give you the "complete"
997
     * representation of the field on the form, whereas Field will give you the core editing widget,
998
     * such as an input tag.
999
     *
1000
     * @param array $properties
1001
     * @return DBHTMLText
1002
     */
1003
    public function Field($properties = array())
1004
    {
1005
        $context = $this;
1006
1007
        $this->extend('onBeforeRender', $context, $properties);
1008
1009
        if (count($properties)) {
1010
            $context = $context->customise($properties);
1011
        }
1012
1013
        $result = $context->renderWith($this->getTemplates());
1014
1015
        // Trim whitespace from the result, so that trailing newlines are supressed. Works for
1016
        // strings and HTMLText values
1017
        if (is_string($result)) {
1018
            $result = trim($result);
1019
        } elseif ($result instanceof DBField) {
1020
            $result->setValue(trim($result->getValue()));
1021
        }
1022
1023
        return $result;
1024
    }
1025
1026
    /**
1027
     * Returns a "field holder" for this field.
1028
     *
1029
     * Forms are constructed by concatenating a number of these field holders.
1030
     *
1031
     * The default field holder is a label and a form field inside a div.
1032
     *
1033
     * @see FieldHolder.ss
1034
     *
1035
     * @param array $properties
1036
     *
1037
     * @return DBHTMLText
1038
     */
1039
    public function FieldHolder($properties = array())
1040
    {
1041
        $context = $this;
1042
1043
        $this->extend('onBeforeRenderHolder', $context, $properties);
1044
1045
        if (count($properties)) {
1046
            $context = $this->customise($properties);
1047
        }
1048
1049
        return $context->renderWith($this->getFieldHolderTemplates());
1050
    }
1051
1052
    /**
1053
     * Returns a restricted field holder used within things like FieldGroups.
1054
     *
1055
     * @param array $properties
1056
     *
1057
     * @return string
1058
     */
1059
    public function SmallFieldHolder($properties = array())
1060
    {
1061
        $context = $this;
1062
1063
        if (count($properties)) {
1064
            $context = $this->customise($properties);
1065
        }
1066
1067
        return $context->renderWith($this->getSmallFieldHolderTemplates());
1068
    }
1069
1070
    /**
1071
     * Returns an array of templates to use for rendering {@link FieldHolder}.
1072
     *
1073
     * @return array
1074
     */
1075
    public function getTemplates()
1076
    {
1077
        return $this->_templates($this->getTemplate());
1078
    }
1079
1080
    /**
1081
     * Returns an array of templates to use for rendering {@link FieldHolder}.
1082
     *
1083
     * @return array
1084
     */
1085
    public function getFieldHolderTemplates()
1086
    {
1087
        return $this->_templates(
1088
            $this->getFieldHolderTemplate(),
1089
            '_holder'
1090
        );
1091
    }
1092
1093
    /**
1094
     * Returns an array of templates to use for rendering {@link SmallFieldHolder}.
1095
     *
1096
     * @return array
1097
     */
1098
    public function getSmallFieldHolderTemplates()
1099
    {
1100
        return $this->_templates(
1101
            $this->getSmallFieldHolderTemplate(),
1102
            '_holder_small'
1103
        );
1104
    }
1105
1106
1107
    /**
1108
     * Generate an array of class name strings to use for rendering this form field into HTML.
1109
     *
1110
     * @param string $customTemplate
1111
     * @param string $customTemplateSuffix
1112
     *
1113
     * @return array
1114
     */
1115
    protected function _templates($customTemplate = null, $customTemplateSuffix = null)
1116
    {
1117
        $templates = SSViewer::get_templates_by_class(static::class, $customTemplateSuffix, __CLASS__);
1118
        // Prefer any custom template
1119
        if ($customTemplate) {
1120
            // Prioritise direct template
1121
            array_unshift($templates, $customTemplate);
1122
        }
1123
        return $templates;
1124
    }
1125
1126
    /**
1127
     * Returns true if this field is a composite field.
1128
     *
1129
     * To create composite field types, you should subclass {@link CompositeField}.
1130
     *
1131
     * @return bool
1132
     */
1133
    public function isComposite()
1134
    {
1135
        return false;
1136
    }
1137
1138
    /**
1139
     * Returns true if this field has its own data.
1140
     *
1141
     * Some fields, such as titles and composite fields, don't actually have any data. It doesn't
1142
     * make sense for data-focused methods to look at them. By overloading hasData() to return
1143
     * false, you can prevent any data-focused methods from looking at it.
1144
     *
1145
     * @see FieldList::collateDataFields()
1146
     *
1147
     * @return bool
1148
     */
1149
    public function hasData()
1150
    {
1151
        return true;
1152
    }
1153
1154
    /**
1155
     * @return bool
1156
     */
1157
    public function isReadonly()
1158
    {
1159
        return $this->readonly;
1160
    }
1161
1162
    /**
1163
     * Sets a read-only flag on this FormField.
1164
     *
1165
     * Use performReadonlyTransformation() to transform this instance.
1166
     *
1167
     * Setting this to false has no effect on the field.
1168
     *
1169
     * @param bool $readonly
1170
     *
1171
     * @return $this
1172
     */
1173
    public function setReadonly($readonly)
1174
    {
1175
        $this->readonly = $readonly;
1176
        return $this;
1177
    }
1178
1179
    /**
1180
     * @return bool
1181
     */
1182
    public function isDisabled()
1183
    {
1184
        return $this->disabled;
1185
    }
1186
1187
    /**
1188
     * Sets a disabled flag on this FormField.
1189
     *
1190
     * Use performDisabledTransformation() to transform this instance.
1191
     *
1192
     * Setting this to false has no effect on the field.
1193
     *
1194
     * @param bool $disabled
1195
     *
1196
     * @return $this
1197
     */
1198
    public function setDisabled($disabled)
1199
    {
1200
        $this->disabled = $disabled;
1201
1202
        return $this;
1203
    }
1204
1205
    /**
1206
     * @return bool
1207
     */
1208
    public function isAutofocus()
1209
    {
1210
        return $this->autofocus;
1211
    }
1212
1213
    /**
1214
     * Sets a autofocus flag on this FormField.
1215
     *
1216
     * @param bool $autofocus
1217
     * @return $this
1218
     */
1219
    public function setAutofocus($autofocus)
1220
    {
1221
        $this->autofocus = $autofocus;
1222
        return $this;
1223
    }
1224
1225
    /**
1226
     * Returns a read-only version of this field.
1227
     *
1228
     * @return FormField
1229
     */
1230
    public function performReadonlyTransformation()
1231
    {
1232
        $readonlyClassName = static::class . '_Readonly';
1233
1234
        if (ClassInfo::exists($readonlyClassName)) {
1235
            $clone = $this->castedCopy($readonlyClassName);
1236
        } else {
1237
            $clone = $this->castedCopy(ReadonlyField::class);
1238
        }
1239
1240
        $clone->setReadonly(true);
1241
1242
        return $clone;
1243
    }
1244
1245
    /**
1246
     * Return a disabled version of this field.
1247
     *
1248
     * Tries to find a class of the class name of this field suffixed with "_Disabled", failing
1249
     * that, finds a method {@link setDisabled()}.
1250
     *
1251
     * @return FormField
1252
     */
1253
    public function performDisabledTransformation()
1254
    {
1255
        $disabledClassName = static::class . '_Disabled';
1256
1257
        if (ClassInfo::exists($disabledClassName)) {
1258
            $clone = $this->castedCopy($disabledClassName);
1259
        } else {
1260
            $clone = clone $this;
1261
        }
1262
1263
        $clone->setDisabled(true);
1264
1265
        return $clone;
1266
    }
1267
1268
    /**
1269
     * @param FormTransformation $transformation
1270
     *
1271
     * @return mixed
1272
     */
1273
    public function transform(FormTransformation $transformation)
1274
    {
1275
        return $transformation->transform($this);
1276
    }
1277
1278
    /**
1279
     * Returns whether the current field has the given class added
1280
     *
1281
     * @param string $class
1282
     *
1283
     * @return bool
1284
     */
1285
    public function hasClass($class)
1286
    {
1287
        $classes = explode(' ', strtolower($this->extraClass()));
1288
        return in_array(strtolower(trim($class)), $classes);
1289
    }
1290
1291
    /**
1292
     * Returns the field type.
1293
     *
1294
     * The field type is the class name with the word Field dropped off the end, all lowercase.
1295
     *
1296
     * It's handy for assigning HTML classes. Doesn't signify the input type attribute.
1297
     *
1298
     * @see {link getAttributes()}.
1299
     *
1300
     * @return string
1301
     */
1302
    public function Type()
1303
    {
1304
        $type = new ReflectionClass($this);
1305
        return strtolower(preg_replace('/Field$/', '', $type->getShortName()));
1306
    }
1307
1308
    /**
1309
     * Abstract method each {@link FormField} subclass must implement, determines whether the field
1310
     * is valid or not based on the value.
1311
     *
1312
     * @todo Make this abstract.
1313
     *
1314
     * @param Validator $validator
1315
     * @return bool
1316
     */
1317
    public function validate($validator)
0 ignored issues
show
Unused Code introduced by
The parameter $validator is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

1317
    public function validate(/** @scrutinizer ignore-unused */ $validator)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
1318
    {
1319
        return true;
1320
    }
1321
1322
    /**
1323
     * Describe this field, provide help text for it.
1324
     *
1325
     * By default, renders as a span class="description" underneath the form field.
1326
     *
1327
     * @param string $description
1328
     *
1329
     * @return $this
1330
     */
1331
    public function setDescription($description)
1332
    {
1333
        $this->description = $description;
1334
1335
        return $this;
1336
    }
1337
1338
    /**
1339
     * @return string
1340
     */
1341
    public function getDescription()
1342
    {
1343
        return $this->description;
1344
    }
1345
1346
    /**
1347
     * @return string
1348
     */
1349
    public function debug()
1350
    {
1351
        $strValue = is_string($this->value) ? $this->value : print_r($this->value, true);
1352
1353
        return sprintf(
1354
            '%s (%s: %s : <span style="color:red;">%s</span>) = %s',
1355
            Convert::raw2att(static::class),
1356
            Convert::raw2att($this->name),
1357
            Convert::raw2att($this->title),
1358
            $this->getMessageCast() == ValidationResult::CAST_HTML ? Convert::raw2xml($this->message) : $this->message,
1359
            Convert::raw2att($strValue)
0 ignored issues
show
Bug introduced by
It seems like SilverStripe\Core\Convert::raw2att($strValue) can also be of type array and array; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1359
            /** @scrutinizer ignore-type */ Convert::raw2att($strValue)
Loading history...
1360
        );
1361
    }
1362
1363
    /**
1364
     * This function is used by the template processor. If you refer to a field as a $ variable, it
1365
     * will return the $Field value.
1366
     *
1367
     * @return string
1368
     */
1369
    public function forTemplate()
1370
    {
1371
        return $this->Field();
1372
    }
1373
1374
    /**
1375
     * @return bool
1376
     */
1377
    public function Required()
1378
    {
1379
        if ($this->form && ($validator = $this->form->getValidator())) {
1380
            return $validator->fieldIsRequired($this->name);
1381
        }
1382
1383
        return false;
1384
    }
1385
1386
    /**
1387
     * Set the FieldList that contains this field.
1388
     *
1389
     * @param FieldList $containerFieldList
1390
     * @return $this
1391
     */
1392
    public function setContainerFieldList($containerFieldList)
1393
    {
1394
        $this->containerFieldList = $containerFieldList;
1395
        return $this;
1396
    }
1397
1398
    /**
1399
     * Get the FieldList that contains this field.
1400
     *
1401
     * @return FieldList
1402
     */
1403
    public function getContainerFieldList()
1404
    {
1405
        return $this->containerFieldList;
1406
    }
1407
1408
    /**
1409
     * @return null|FieldList
1410
     */
1411
    public function rootFieldList()
1412
    {
1413
        if ($this->containerFieldList) {
1414
            return $this->containerFieldList->rootFieldList();
1415
        }
1416
1417
        $class = static::class;
1418
        user_error(
1419
            "rootFieldList() called on {$class} object without a containerFieldList",
1420
            E_USER_ERROR
1421
        );
1422
1423
        return null;
1424
    }
1425
1426
    /**
1427
     * Returns another instance of this field, but "cast" to a different class. The logic tries to
1428
     * retain all of the instance properties, and may be overloaded by subclasses to set additional
1429
     * ones.
1430
     *
1431
     * Assumes the standard FormField parameter signature with its name as the only mandatory
1432
     * argument. Mainly geared towards creating *_Readonly or *_Disabled subclasses of the same
1433
     * type, or casting to a {@link ReadonlyField}.
1434
     *
1435
     * Does not copy custom field templates, since they probably won't apply to the new instance.
1436
     *
1437
     * @param mixed $classOrCopy Class name for copy, or existing copy instance to update
1438
     *
1439
     * @return FormField
1440
     */
1441
    public function castedCopy($classOrCopy)
1442
    {
1443
        $field = $classOrCopy;
1444
1445
        if (!is_object($field)) {
1446
            $field = $classOrCopy::create($this->name);
1447
        }
1448
1449
        $field
1450
            ->setValue($this->value)
1451
            ->setForm($this->form)
1452
            ->setTitle($this->Title())
1453
            ->setLeftTitle($this->LeftTitle())
1454
            ->setRightTitle($this->RightTitle())
1455
            ->addExtraClass($this->extraClass) // Don't use extraClass(), since this merges calculated values
1456
            ->setDescription($this->getDescription());
1457
1458
        // Only include built-in attributes, ignore anything set through getAttributes().
1459
        // Those might change important characteristics of the field, e.g. its "type" attribute.
1460
        foreach ($this->attributes as $attributeKey => $attributeValue) {
1461
            $field->setAttribute($attributeKey, $attributeValue);
1462
        }
1463
1464
        return $field;
1465
    }
1466
1467
    /**
1468
     * Determine if the value of this formfield accepts front-end submitted values and is saveable.
1469
     *
1470
     * @return bool
1471
     */
1472
    public function canSubmitValue()
1473
    {
1474
        return $this->hasData() && !$this->isReadonly() && !$this->isDisabled();
1475
    }
1476
1477
    /**
1478
     * Sets the component type the FormField will be rendered as on the front-end.
1479
     *
1480
     * @param string $componentType
1481
     * @return FormField
1482
     */
1483
    public function setSchemaComponent($componentType)
1484
    {
1485
        $this->schemaComponent = $componentType;
1486
        return $this;
1487
    }
1488
1489
    /**
1490
     * Gets the type of front-end component the FormField will be rendered as.
1491
     *
1492
     * @return string
1493
     */
1494
    public function getSchemaComponent()
1495
    {
1496
        return $this->schemaComponent;
1497
    }
1498
1499
    /**
1500
     * Sets the schema data used for rendering the field on the front-end.
1501
     * Merges the passed array with the current `$schemaData` or {@link getSchemaDataDefaults()}.
1502
     * Any passed keys that are not defined in {@link getSchemaDataDefaults()} are ignored.
1503
     * If you want to pass around ad hoc data use the `data` array e.g. pass `['data' => ['myCustomKey' => 'yolo']]`.
1504
     *
1505
     * @param array $schemaData - The data to be merged with $this->schemaData.
1506
     * @return FormField
1507
     *
1508
     * @todo Add deep merging of arrays like `data` and `attributes`.
1509
     */
1510
    public function setSchemaData($schemaData = [])
1511
    {
1512
        $defaults = $this->getSchemaData();
1513
        $this->schemaData = array_merge($this->schemaData, array_intersect_key($schemaData, $defaults));
1514
        return $this;
1515
    }
1516
1517
    /**
1518
     * Gets the schema data used to render the FormField on the front-end.
1519
     *
1520
     * @return array
1521
     */
1522
    public function getSchemaData()
1523
    {
1524
        $defaults = $this->getSchemaDataDefaults();
1525
        return array_replace_recursive($defaults, array_intersect_key($this->schemaData, $defaults));
1526
    }
1527
1528
    /**
1529
     * @todo Throw exception if value is missing, once a form field schema is mandatory across the CMS
1530
     *
1531
     * @return string
1532
     */
1533
    public function getSchemaDataType()
1534
    {
1535
        return $this->schemaDataType;
1536
    }
1537
1538
    /**
1539
     * Gets the defaults for $schemaData.
1540
     * The keys defined here are immutable, meaning undefined keys passed to {@link setSchemaData()} are ignored.
1541
     * Instead the `data` array should be used to pass around ad hoc data.
1542
     *
1543
     * @return array
1544
     */
1545
    public function getSchemaDataDefaults()
1546
    {
1547
        return [
1548
            'name' => $this->getName(),
1549
            'id' => $this->ID(),
1550
            'type' => $this->getInputType(),
1551
            'schemaType' => $this->getSchemaDataType(),
1552
            'component' => $this->getSchemaComponent(),
1553
            'holderId' => $this->HolderID(),
1554
            'title' => $this->obj('Title')->getSchemaValue(),
1555
            'source' => null,
1556
            'extraClass' => $this->extraClass(),
1557
            'description' => $this->obj('Description')->getSchemaValue(),
1558
            'rightTitle' => $this->obj('RightTitle')->getSchemaValue(),
1559
            'leftTitle' => $this->obj('LeftTitle')->getSchemaValue(),
1560
            'readOnly' => $this->isReadonly(),
1561
            'disabled' => $this->isDisabled(),
1562
            'customValidationMessage' => $this->getCustomValidationMessage(),
1563
            'validation' => $this->getSchemaValidation(),
1564
            'attributes' => [],
1565
            'autoFocus' => $this->isAutofocus(),
1566
            'data' => [],
1567
        ];
1568
    }
1569
1570
    /**
1571
     * Sets the schema data used for rendering the field on the front-end.
1572
     * Merges the passed array with the current `$schemaState` or {@link getSchemaStateDefaults()}.
1573
     * Any passed keys that are not defined in {@link getSchemaStateDefaults()} are ignored.
1574
     * If you want to pass around ad hoc data use the `data` array e.g. pass `['data' => ['myCustomKey' => 'yolo']]`.
1575
     *
1576
     * @param array $schemaState The data to be merged with $this->schemaData.
1577
     * @return FormField
1578
     *
1579
     * @todo Add deep merging of arrays like `data` and `attributes`.
1580
     */
1581
    public function setSchemaState($schemaState = [])
1582
    {
1583
        $defaults = $this->getSchemaState();
1584
        $this->schemaState = array_merge($this->schemaState, array_intersect_key($schemaState, $defaults));
1585
        return $this;
1586
    }
1587
1588
    /**
1589
     * Gets the schema state used to render the FormField on the front-end.
1590
     *
1591
     * @return array
1592
     */
1593
    public function getSchemaState()
1594
    {
1595
        $defaults = $this->getSchemaStateDefaults();
1596
        return array_merge($defaults, array_intersect_key($this->schemaState, $defaults));
1597
    }
1598
1599
    /**
1600
     * Gets the defaults for $schemaState.
1601
     * The keys defined here are immutable, meaning undefined keys passed to {@link setSchemaState()} are ignored.
1602
     * Instead the `data` array should be used to pass around ad hoc data.
1603
     * Includes validation data if the field is associated to a {@link Form},
1604
     * and {@link Form->validate()} has been called.
1605
     *
1606
     * @todo Make form / field messages not always stored as html; Store value / casting as separate values.
1607
     * @return array
1608
     */
1609
    public function getSchemaStateDefaults()
1610
    {
1611
        $state = [
1612
            'name' => $this->getName(),
1613
            'id' => $this->ID(),
1614
            'value' => $this->Value(),
1615
            'message' => $this->getSchemaMessage(),
1616
            'data' => [],
1617
        ];
1618
1619
        return $state;
1620
    }
1621
1622
    /**
1623
     * Return list of validation rules. Each rule is a key value pair.
1624
     * The key is the rule name. The value is any information the frontend
1625
     * validation handler can understand, or just `true` to enable.
1626
     *
1627
     * @return array
1628
     */
1629
    public function getSchemaValidation()
1630
    {
1631
        $validationList = [];
1632
        if ($this->Required()) {
1633
            $validationList['required'] = true;
1634
        }
1635
        $this->extend('updateSchemaValidation', $validationList);
1636
        return $validationList;
1637
    }
1638
}
1639