-
Notifications
You must be signed in to change notification settings - Fork 610
/
FieldInterface.php
588 lines (544 loc) · 21.6 KB
/
FieldInterface.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
<?php
/**
* @link https://craftcms.com/
* @copyright Copyright (c) Pixel & Tonic, Inc.
* @license https://craftcms.github.io/license/
*/
namespace craft\base;
use craft\elements\db\ElementQueryInterface;
use craft\enums\AttributeStatus;
use craft\models\GqlSchema;
use GraphQL\Type\Definition\Type;
use yii\base\Component as YiiComponent;
use yii\db\ExpressionInterface;
use yii\validators\Validator;
/**
* FieldInterface defines the common interface to be implemented by field classes.
* A class implementing this interface should also use [[SavableComponentTrait]] and [[FieldTrait]].
*
* @mixin FieldTrait
* @mixin YiiComponent
* @mixin Model
* @mixin SavableComponentTrait
* @phpstan-require-extends Field
* @author Pixel & Tonic, Inc. <support@pixelandtonic.com>
* @since 3.0.0
*/
interface FieldInterface extends SavableComponentInterface, Chippable
{
/**
* Returns the field type’s SVG icon.
*
* The returned icon can be a system icon’s name (e.g. `'whiskey-glass-ice'`),
* the path to an SVG file, or raw SVG markup.
*
* System icons can be found in `src/icons/solid/.`
*
* @return string
* @since 5.0.0
*/
public static function icon(): string;
/**
* Returns whether the field can be included multiple times within a field layout.
*
* @return bool
* @since 5.0.0
*/
public static function isMultiInstance(): bool;
/**
* Returns whether the field can be marked as required.
*
* @return bool
* @since 4.0.0
*/
public static function isRequirable(): bool;
/**
* Returns which translation methods the field supports.
*
* This method should return an array with at least one of the following values:
* - 'none' (values will always be copied to other sites)
* - 'language' (values will be copied to other sites with the same language)
* - 'site' (values will never be copied to other sites)
* - 'custom' (values will be copied/not copied depending on a custom translation key)
*
* @return string[]
* @see getTranslationKey()
*/
public static function supportedTranslationMethods(): array;
/**
* Returns the PHPDoc type this field’s values will have.
*
* It will be used by the generated `CustomFieldBehavior` class.
*
* If the values can be of more than one type, return multiple types separated by `|`s.
*
* ```php
* public static function valueType(): string
* {
* return 'int|string';
* }
* ```
*
* @return string
* @since 5.0.0
*/
public static function phpType(): string;
/**
* Returns the DB data type(s) that this field will store within the `elements_sites.content` column.
*
* ```php
* return \yii\db\Schema::TYPE_STRING;
* ```
*
* Specifying the DB type isn’t strictly necessary, but it enables individual field values to be targeted
* by functional indexes.
*
* If field values will consist of an associative array, each of the array keys can be specified here,
* so the nested values can receive their own functional indexes.
*
* ```php
* return [
* 'date' => \yii\db\Schema::TYPE_DATETIME,
* 'tz' => \yii\db\Schema::TYPE_STRING,
* ];
* ```
*
* If `null` is returned, the field’s values won’t be stored in the `elements_sites.content` column at all.
* In that case, the field will be solely responsible for storing and retrieving its own values from
* [[normalizeValue()]] and [[afterElementSave()]]/[[afterElementPropagate()]].
*
* @return string|string[]|null The column type(s).
*/
public static function dbType(): array|string|null;
/**
* Returns a query builder-compatible condition for the given field instances, for a user-provided param value.
*
* If `false` is returned, an always-false condition will be used.
*
* @param static[] $instances The field instances to search
* @param mixed $value The user-supplied param value
* @param array $params Additional parameters that should be bound to the query via [[\yii\db\Query::addParams()]]
* @return array|string|ExpressionInterface|false|null
* @since 5.0.0
*/
public static function queryCondition(
array $instances,
mixed $value,
array &$params,
): array|string|ExpressionInterface|false|null;
/**
* Returns the orientation the field should use (`ltr` or `rtl`).
*
* @param ElementInterface|null $element The element being edited
* @return string
* @since 3.7.5
*/
public function getOrientation(?ElementInterface $element): string;
/**
* Returns whether the field should be shown as translatable in the UI.
*
* Note this method has no effect on whether the field’s value will get copied over to other
* sites when the element is actually getting saved. That is determined by [[getTranslationKey()]].
*
* @param ElementInterface|null $element The element being edited
* @return bool
*/
public function getIsTranslatable(?ElementInterface $element): bool;
/**
* Returns the description of this field’s translation support.
*
* @param ElementInterface|null $element The element being edited
* @return string|null
* @since 3.4.0
*/
public function getTranslationDescription(?ElementInterface $element): ?string;
/**
* Returns the field’s translation key, based on a given element.
*
* When saving an element on a multi-site Craft install, if `$propagate` is `true` for [[\craft\services\Elements::saveElement()]],
* then `getTranslationKey()` will be called for each custom field and for each site the element should be propagated to.
* If the method returns the same value as it did for the initial site, then the initial site’s value will be copied over
* to the target site.
*
* @param ElementInterface $element The element being saved
* @return string The translation key
*/
public function getTranslationKey(ElementInterface $element): string;
/**
* Returns the status of the field for a given element.
*
* If the field has a known status, an array should be returned with two elements:
*
* - A [[\craft\enums\AttributeStatus]] case
* - The status label
*
* For example:
*
* ```php
* return [AttributeStatus::Modified, 'The field has been modified.');
* ```
*
* @param ElementInterface $element
* @return array{0:AttributeStatus|value-of<AttributeStatus>,1:string}|null
* @since 3.7.0
*/
public function getStatus(ElementInterface $element): ?array;
/**
* Returns the input’s ID, which the `<label>`’s `for` attribute should reference.
*
* @return string
* @since 3.7.32
*/
public function getInputId(): string;
/**
* Returns the input’s label ID.
*
* @return string
* @since 4.1.0
*/
public function getLabelId(): string;
/**
* Returns whether the field should use a `<fieldset>` + `<legend>` instead of a `<div>` + `<label>`.
*
* @return bool
* @since 3.6.0
*/
public function useFieldset(): bool;
/**
* Returns the field’s input HTML.
*
* An extremely simple implementation would be to directly return some HTML:
*
* ```php
* return '<textarea name="'.$name.'">'.$value.'</textarea>';
* ```
*
* For more complex inputs, you might prefer to create a template, and render it via
* [[\craft\web\View::renderTemplate()]]. For example, the following code would render a template located at
* `path/to/myplugin/templates/_fieldinput.html`, passing the `$name` and `$value` variables to it:
*
* ```php
* return Craft::$app->view->renderTemplate('myplugin/_fieldinput', [
* 'name' => $name,
* 'value' => $value
* ]);
* ```
*
* If you need to tie any JavaScript code to your input, it’s important to know that any `name` and `id`
* attributes within the returned HTML will probably get [[\craft\helpers\Html::namespaceHtml()|namespaced]],
* however your JavaScript code will be left untouched.
* For example, if getInputHtml() returns the following HTML:
*
* ```html
* <textarea id="foo" name="foo"></textarea>
* <script type="text/javascript">
* var textarea = document.getElementById('foo');
* </script>
* ```
*
* …then it might actually look like this before getting output to the browser:
*
* ```html
* <textarea id="namespace-foo" name="namespace[foo]"></textarea>
* <script type="text/javascript">
* var textarea = document.getElementById('foo');
* </script>
* ```
*
* As you can see, that JavaScript code will not be able to find the textarea, because the textarea’s `id`
* attribute was changed from `foo` to `namespace-foo`.
* Before you start adding `namespace-` to the beginning of your element ID selectors, keep in mind that the actual
* namespace is going to change depending on the context. Often they are randomly generated. So it’s not quite
* that simple.
*
* Thankfully, Craft provides a couple handy methods that can help you deal with this:
*
* - [[\craft\helpers\Html::id()]] will generate a valid element ID from an input name.
* - [[\craft\web\View::namespaceInputId()]] will give you the namespaced version of a given ID.
* - [[\craft\web\View::namespaceInputName()]] will give you the namespaced version of a given input name.
*
* So here’s what a getInputHtml() method that includes field-targeting JavaScript code might look like:
*
* ```php
* public function getInputHtml($value, $element): string
* {
* // Generate a valid ID based on the input name
* $id = craft\helpers\Html::id($name);
* // Figure out what that ID is going to be namespaced into
* $namespacedId = Craft::$app->view->namespaceInputId($id);
* // Render and return the input template
* return Craft::$app->view->renderTemplate('myplugin/_fieldinput', [
* 'name' => $name,
* 'id' => $id,
* 'namespacedId' => $namespacedId,
* 'value' => $value,
* ]);
* }
* ```
*
* And the _fieldinput.html template might look like this:
*
* ```twig
* <textarea id="{{ id }}" name="{{ name }}">{{ value }}</textarea>
* <script type="text/javascript">
* var textarea = document.getElementById('{{ namespacedId }}');
* </script>
* ```
*
* The same principles also apply if you’re including your JavaScript code with
* [[\craft\web\View::registerJs()]].
*
* @param mixed $value The field’s value. This will either be the [[normalizeValue()|normalized value]],
* raw POST data (i.e. if there was a validation error), or null
* @param ElementInterface|null $element The element the field is associated with, if there is one
* @return string The input HTML.
*/
public function getInputHtml(mixed $value, ?ElementInterface $element): string;
/**
* Returns a static (non-editable) version of the field’s input HTML.
*
* This function is called to output field values when viewing element drafts.
*
* @param mixed $value The field’s value
* @param ElementInterface $element The element the field is associated with
* @return string The static version of the field’s input HTML
*/
public function getStaticHtml(mixed $value, ElementInterface $element): string;
/**
* Returns the validation rules for an element with this field.
*
* Rules should be defined in the array syntax required by [[\yii\base\Model::rules()]],
* with one difference: you can skip the first argument (the attribute list).
*
* ```php
* [
* // explicitly specify the field attribute
* [$this->handle, 'string', 'min' => 3, 'max' => 12],
* // skip the field attribute
* ['string', 'min' => 3, 'max' => 12],
* // you can only pass the validator class name/handle if not setting any params
* 'bool',
* ]
* ```
*
* To register validation rules that should only be enforced for _live_ elements,
* set the rule [scenario](https://www.yiiframework.com/doc/guide/2.0/en/structure-models#scenarios)
* to `live`:
*
* ```php
* [
* ['string', 'min' => 3, 'max' => 12, 'on' => \craft\base\Element::SCENARIO_LIVE],
* ]
* ```
*
* @return array
*/
public function getElementValidationRules(): array;
/**
* Returns whether the given value should be considered “empty” to a validator.
*
* @param mixed $value The field’s value
* @param ElementInterface $element The element the field is associated with, if there is one
* @return bool Whether the value should be considered “empty”
* @see Validator::$isEmpty
*/
public function isValueEmpty(mixed $value, ElementInterface $element): bool;
/**
* Returns the search keywords that should be associated with this field.
*
* The keywords can be separated by commas and/or whitespace; it doesn’t really matter. [[\craft\services\Search]]
* will be able to find the individual keywords in whatever string is returned, and normalize them for you.
*
* @param mixed $value The field’s value
* @param ElementInterface $element The element the field is associated with, if there is one
* @return string A string of search keywords.
*/
public function getSearchKeywords(mixed $value, ElementInterface $element): string;
/**
* Normalizes the field’s value for use.
*
* This method is called when the field’s value is first accessed from the element. For example, the first time
* `element.myFieldHandle` is called from a template, or right before [[getInputHtml()]] is called. Whatever
* this method returns is what `element.myFieldHandle` will likewise return, and what [[getInputHtml()]]’s and
* [[serializeValue()]]’s $value arguments will be set to.
*
* The value passed into this method will vary depending on the context.
*
* - If a new, unsaved element is being edited for the first time (such as an entry within a Quick Post widget
* on the Dashboard), the value will be `null`.
* - If an element is currently being saved, the value will be the field’s POST data.
* - If an existing element was retrieved from the database, the value will be whatever is stored in the field’s
* `content` table column. (Or if the field doesn’t have a `content` table column per [[hasContentColumn()]],
* the value will be `null`.)
* - If the field is being cleared out (e.g. via the `resave/entries` command with `--to :empty:`),
* the value will be an empty string (`''`).
*
* There are cases where a pre-normalized value could be passed in as well, so be sure to account for that.
*
* @param mixed $value The raw field value
* @param ElementInterface|null $element The element the field is associated with, if there is one
* @return mixed The prepared field value
*/
public function normalizeValue(mixed $value, ?ElementInterface $element): mixed;
/**
* Normalizes a posted field value for use.
*
* This should call [[normalizeValue()]] by default, unless there are any special considerations that
* need to be made for posted values.
*
* @param mixed $value The serialized value
* @param ElementInterface|null $element The element the field is associated with, if there is one
* @return mixed The prepared field value
* @since 4.5.0
*/
public function normalizeValueFromRequest(mixed $value, ?ElementInterface $element): mixed;
/**
* Prepares the field’s value to be stored somewhere, like the content table.
*
* Data types that are JSON-encodable are safe (arrays, integers, strings, booleans, etc).
* Whatever this returns should be something [[normalizeValue()]] can handle.
*
* @param mixed $value The raw field value
* @param ElementInterface|null $element The element the field is associated with, if there is one
* @return mixed The serialized field value
*/
public function serializeValue(mixed $value, ?ElementInterface $element): mixed;
/**
* Copies the field’s value from one element to another.
*
* @param ElementInterface $from
* @param ElementInterface $to
* @since 3.7.0
*/
public function copyValue(ElementInterface $from, ElementInterface $to): void;
/**
* Returns the element condition rule class that should be used for this field.
*
* The rule class must be an instance of [[\craft\fields\conditions\FieldConditionRuleInterface]].
*
* @return string|array|null
* @phpstan-return string|array{class:string}|null
*/
public function getElementConditionRuleType(): array|string|null;
/**
* Returns a SQL expression which extracts the field’s value from the `elements_sites.content` column.
*
* @param string|null $key The data key to fetch, if this field stores multiple values
* @return string|null
* @since 5.0.0
*/
public function getValueSql(?string $key = null): ?string;
/**
* Modifies an element index query.
*
* This method will be called whenever an element index is being loaded,
* which contains a column for this field.
*
* @param ElementQueryInterface $query The element query
* @since 3.0.9
*/
public function modifyElementIndexQuery(ElementQueryInterface $query): void;
/**
* Sets whether the field is fresh.
*
* @param bool|null $isFresh Whether the field is fresh.
*/
public function setIsFresh(?bool $isFresh = null): void;
/**
* Returns whether the field should be included in the given GraphQL schema.
*
* @param GqlSchema $schema
* @return bool
* @since 3.6.0
*/
public function includeInGqlSchema(GqlSchema $schema): bool;
/**
* Returns the GraphQL type to be used for this field type.
*
* @return Type|array
* @since 3.3.0
*/
public function getContentGqlType(): Type|array;
/**
* Returns the GraphQL type to be used as an argument in mutations for this field type.
*
* @return Type|array
* @since 3.5.0
*/
public function getContentGqlMutationArgumentType(): Type|array;
/**
* Returns the GraphQL type to be used as an argument in queries for this field type.
*
* @return Type|array
* @since 3.5.0
*/
public function getContentGqlQueryArgumentType(): Type|array;
// Events
// -------------------------------------------------------------------------
/**
* Performs actions before an element is saved.
*
* @param ElementInterface $element The element that is about to be saved
* @param bool $isNew Whether the element is brand new
* @return bool Whether the element should be saved
*/
public function beforeElementSave(ElementInterface $element, bool $isNew): bool;
/**
* Performs actions after the element has been saved.
*
* @param ElementInterface $element The element that was just saved
* @param bool $isNew Whether the element is brand new
*/
public function afterElementSave(ElementInterface $element, bool $isNew): void;
/**
* Performs actions after the element has been fully saved and propagated to other sites.
*
* @param ElementInterface $element The element that was just saved and propagated
* @param bool $isNew Whether the element is brand new
* @since 3.2.0
*/
public function afterElementPropagate(ElementInterface $element, bool $isNew): void;
/**
* Performs actions before an element is deleted.
*
* @param ElementInterface $element The element that is about to be deleted
* @return bool Whether the element should be deleted
*/
public function beforeElementDelete(ElementInterface $element): bool;
/**
* Performs actions after the element has been deleted.
*
* @param ElementInterface $element The element that was just deleted
*/
public function afterElementDelete(ElementInterface $element): void;
/**
* Performs actions before an element is deleted for a site.
*
* @param ElementInterface $element The element that is about to be deleted
* @return bool Whether the element should be deleted for a site
* @since 4.7.0
*/
public function beforeElementDeleteForSite(ElementInterface $element): bool;
/**
* Performs actions after the element has been deleted.
*
* @param ElementInterface $element The element that was just deleted for a site
* @since 4.7.0
*/
public function afterElementDeleteForSite(ElementInterface $element): void;
/**
* Performs actions before an element is restored.
*
* @param ElementInterface $element The element that is about to be restored
* @return bool Whether the element should be restored
* @since 3.1.0
*/
public function beforeElementRestore(ElementInterface $element): bool;
/**
* Performs actions after the element has been restored.
*
* @param ElementInterface $element The element that was just restored
* @since 3.1.0
*/
public function afterElementRestore(ElementInterface $element): void;
}