0d58b65614d0c10b0f6435e654b8484feb601742
[yaffs-website] / web / core / lib / Drupal / Core / Field / FieldConfigBase.php
1 <?php
2
3 namespace Drupal\Core\Field;
4
5 use Drupal\Core\Config\Entity\ConfigEntityBase;
6 use Drupal\Core\Entity\EntityStorageInterface;
7 use Drupal\Core\Entity\FieldableEntityInterface;
8 use Drupal\Core\Field\TypedData\FieldItemDataDefinition;
9
10 /**
11  * Base class for configurable field definitions.
12  */
13 abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigInterface {
14
15   use FieldInputValueNormalizerTrait;
16
17   /**
18    * The field ID.
19    *
20    * The ID consists of 3 parts: the entity type, bundle and the field name.
21    *
22    * Example: node.article.body, user.user.field_main_image.
23    *
24    * @var string
25    */
26   protected $id;
27
28   /**
29    * The field name.
30    *
31    * @var string
32    */
33   protected $field_name;
34
35   /**
36    * The field type.
37    *
38    * This property is denormalized from the field storage for optimization of
39    * the "entity and render cache hits" critical paths. If not present in the
40    * $values passed to create(), it is populated from the field storage in
41    * postCreate(), and saved in config records so that it is present on
42    * subsequent loads.
43    *
44    * @var string
45    */
46   protected $field_type;
47
48   /**
49    * The name of the entity type the field is attached to.
50    *
51    * @var string
52    */
53   protected $entity_type;
54
55   /**
56    * The name of the bundle the field is attached to.
57    *
58    * @var string
59    */
60   protected $bundle;
61
62   /**
63    * The human-readable label for the field.
64    *
65    * This will be used as the title of Form API elements for the field in entity
66    * edit forms, or as the label for the field values in displayed entities.
67    *
68    * If not specified, this defaults to the field_name (mostly useful for fields
69    * created in tests).
70    *
71    * @var string
72    */
73   protected $label;
74
75   /**
76    * The field description.
77    *
78    * A human-readable description for the field when used with this bundle.
79    * For example, the description will be the help text of Form API elements for
80    * this field in entity edit forms.
81    *
82    * @var string
83    */
84   protected $description = '';
85
86   /**
87    * Field-type specific settings.
88    *
89    * An array of key/value pairs. The keys and default values are defined by the
90    * field type.
91    *
92    * @var array
93    */
94   protected $settings = [];
95
96   /**
97    * Flag indicating whether the field is required.
98    *
99    * TRUE if a value for this field is required when used with this bundle,
100    * FALSE otherwise. Currently, required-ness is only enforced at the Form API
101    * level in entity edit forms, not during direct API saves.
102    *
103    * @var bool
104    */
105   protected $required = FALSE;
106
107   /**
108    * Flag indicating whether the field is translatable.
109    *
110    * Defaults to TRUE.
111    *
112    * @var bool
113    */
114   protected $translatable = TRUE;
115
116   /**
117    * Default field value.
118    *
119    * The default value is used when an entity is created, either:
120    * - through an entity creation form; the form elements for the field are
121    *   prepopulated with the default value.
122    * - through direct API calls (i.e. $entity->save()); the default value is
123    *   added if the $entity object provides no explicit entry (actual values or
124    *   "the field is empty") for the field.
125    *
126    * The default value is expressed as a numerically indexed array of items,
127    * each item being an array of key/value pairs matching the set of 'columns'
128    * defined by the "field schema" for the field type, as exposed in
129    * hook_field_schema(). If the number of items exceeds the cardinality of the
130    * field, extraneous items will be ignored.
131    *
132    * This property is overlooked if the $default_value_callback is non-empty.
133    *
134    * Example for a integer field:
135    * @code
136    * array(
137    *   array('value' => 1),
138    *   array('value' => 2),
139    * )
140    * @endcode
141    *
142    * @var array
143    */
144   protected $default_value = [];
145
146   /**
147    * The name of a callback function that returns default values.
148    *
149    * The function will be called with the following arguments:
150    * - \Drupal\Core\Entity\FieldableEntityInterface $entity
151    *   The entity being created.
152    * - \Drupal\Core\Field\FieldDefinitionInterface $definition
153    *   The field definition.
154    * It should return an array of default values, in the same format as the
155    * $default_value property.
156    *
157    * This property takes precedence on the list of fixed values specified in the
158    * $default_value property.
159    *
160    * @var string
161    */
162   protected $default_value_callback = '';
163
164   /**
165    * The field storage object.
166    *
167    * @var \Drupal\Core\Field\FieldStorageDefinitionInterface
168    */
169   protected $fieldStorage;
170
171   /**
172    * The data definition of a field item.
173    *
174    * @var \Drupal\Core\Field\TypedData\FieldItemDataDefinition
175    */
176   protected $itemDefinition;
177
178   /**
179    * Array of constraint options keyed by constraint plugin ID.
180    *
181    * @var array
182    */
183   protected $constraints = [];
184
185   /**
186    * Array of property constraint options keyed by property ID. The values are
187    * associative array of constraint options keyed by constraint plugin ID.
188    *
189    * @var array[]
190    */
191   protected $propertyConstraints = [];
192
193   /**
194    * {@inheritdoc}
195    */
196   public function id() {
197     return $this->entity_type . '.' . $this->bundle . '.' . $this->field_name;
198   }
199
200   /**
201    * {@inheritdoc}
202    */
203   public function getName() {
204     return $this->field_name;
205   }
206
207   /**
208    * {@inheritdoc}
209    */
210   public function getType() {
211     return $this->field_type;
212   }
213
214   /**
215    * {@inheritdoc}
216    */
217   public function getTargetEntityTypeId() {
218     return $this->entity_type;
219   }
220
221   /**
222    * {@inheritdoc}
223    */
224   public function getTargetBundle() {
225     return $this->bundle;
226   }
227
228   /**
229    * {@inheritdoc}
230    */
231   public function calculateDependencies() {
232     parent::calculateDependencies();
233     // Add dependencies from the field type plugin. We can not use
234     // self::calculatePluginDependencies() because instantiation of a field item
235     // plugin requires a parent entity.
236     /** @var $field_type_manager \Drupal\Core\Field\FieldTypePluginManagerInterface */
237     $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
238     $definition = $field_type_manager->getDefinition($this->getType());
239     $this->addDependency('module', $definition['provider']);
240     // Plugins can declare additional dependencies in their definition.
241     if (isset($definition['config_dependencies'])) {
242       $this->addDependencies($definition['config_dependencies']);
243     }
244     // Let the field type plugin specify its own dependencies.
245     // @see \Drupal\Core\Field\FieldItemInterface::calculateDependencies()
246     $this->addDependencies($definition['class']::calculateDependencies($this));
247
248     // Create dependency on the bundle.
249     $bundle_config_dependency = $this->entityManager()->getDefinition($this->entity_type)->getBundleConfigDependency($this->bundle);
250     $this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);
251
252     return $this;
253   }
254
255   /**
256    * {@inheritdoc}
257    */
258   public function onDependencyRemoval(array $dependencies) {
259     $changed = parent::onDependencyRemoval($dependencies);
260     $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
261     $definition = $field_type_manager->getDefinition($this->getType());
262     if ($definition['class']::onDependencyRemoval($this, $dependencies)) {
263       $changed = TRUE;
264     }
265     return $changed;
266   }
267
268   /**
269    * {@inheritdoc}
270    */
271   public function postCreate(EntityStorageInterface $storage) {
272     parent::postCreate($storage);
273     // If it was not present in the $values passed to create(), (e.g. for
274     // programmatic creation), populate the denormalized field_type property
275     // from the field storage, so that it gets saved in the config record.
276     if (empty($this->field_type)) {
277       $this->field_type = $this->getFieldStorageDefinition()->getType();
278     }
279   }
280
281   /**
282    * {@inheritdoc}
283    */
284   public function postSave(EntityStorageInterface $storage, $update = TRUE) {
285     // Clear the cache.
286     $this->entityManager()->clearCachedFieldDefinitions();
287
288     // Invalidate the render cache for all affected entities.
289     $entity_type = $this->getFieldStorageDefinition()->getTargetEntityTypeId();
290     if ($this->entityManager()->hasHandler($entity_type, 'view_builder')) {
291       $this->entityManager()->getViewBuilder($entity_type)->resetCache();
292     }
293   }
294
295   /**
296    * {@inheritdoc}
297    */
298   public function getLabel() {
299     return $this->label();
300   }
301
302   /**
303    * {@inheritdoc}
304    */
305   public function setLabel($label) {
306     $this->label = $label;
307     return $this;
308   }
309
310   /**
311    * {@inheritdoc}
312    */
313   public function getDescription() {
314     return $this->description;
315   }
316
317   /**
318    * {@inheritdoc}
319    */
320   public function setDescription($description) {
321     $this->description = $description;
322     return $this;
323   }
324
325   /**
326    * {@inheritdoc}
327    */
328   public function isTranslatable() {
329     // A field can be enabled for translation only if translation is supported.
330     return $this->translatable && $this->getFieldStorageDefinition()->isTranslatable();
331   }
332
333   /**
334    * {@inheritdoc}
335    */
336   public function setTranslatable($translatable) {
337     $this->translatable = $translatable;
338     return $this;
339   }
340
341   /**
342    * {@inheritdoc}
343    */
344   public function getSettings() {
345     return $this->settings + $this->getFieldStorageDefinition()->getSettings();
346   }
347
348   /**
349    * {@inheritdoc}
350    */
351   public function setSettings(array $settings) {
352     $this->settings = $settings + $this->settings;
353     return $this;
354   }
355
356   /**
357    * {@inheritdoc}
358    */
359   public function getSetting($setting_name) {
360     if (array_key_exists($setting_name, $this->settings)) {
361       return $this->settings[$setting_name];
362     }
363     else {
364       return $this->getFieldStorageDefinition()->getSetting($setting_name);
365     }
366   }
367
368   /**
369    * {@inheritdoc}
370    */
371   public function setSetting($setting_name, $value) {
372     $this->settings[$setting_name] = $value;
373     return $this;
374   }
375
376   /**
377    * {@inheritdoc}
378    */
379   public function isRequired() {
380     return $this->required;
381   }
382
383   /**
384    * [@inheritdoc}
385    */
386   public function setRequired($required) {
387     $this->required = $required;
388     return $this;
389   }
390
391   /**
392    * {@inheritdoc}
393    */
394   public function getDefaultValue(FieldableEntityInterface $entity) {
395     // Allow custom default values function.
396     if ($callback = $this->getDefaultValueCallback()) {
397       $value = call_user_func($callback, $entity, $this);
398       $value = $this->normalizeValue($value, $this->getFieldStorageDefinition()->getMainPropertyName());
399     }
400     else {
401       $value = $this->getDefaultValueLiteral();
402     }
403     // Allow the field type to process default values.
404     $field_item_list_class = $this->getClass();
405     return $field_item_list_class::processDefaultValue($value, $entity, $this);
406   }
407
408   /**
409    * {@inheritdoc}
410    */
411   public function getDefaultValueLiteral() {
412     return $this->default_value;
413   }
414
415   /**
416    * {@inheritdoc}
417    */
418   public function setDefaultValue($value) {
419     $this->default_value = $this->normalizeValue($value, $this->getFieldStorageDefinition()->getMainPropertyName());
420     return $this;
421   }
422
423   /**
424    * {@inheritdoc}
425    */
426   public function getDefaultValueCallback() {
427     return $this->default_value_callback;
428   }
429
430   /**
431    * {@inheritdoc}
432    */
433   public function setDefaultValueCallback($callback) {
434     $this->default_value_callback = $callback;
435     return $this;
436   }
437
438   /**
439    * Implements the magic __sleep() method.
440    *
441    * Using the Serialize interface and serialize() / unserialize() methods
442    * breaks entity forms in PHP 5.4.
443    * @todo Investigate in https://www.drupal.org/node/2074253.
444    */
445   public function __sleep() {
446     // Only serialize necessary properties, excluding those that can be
447     // recalculated.
448     $properties = get_object_vars($this);
449     unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['original']);
450     return array_keys($properties);
451   }
452
453   /**
454    * {@inheritdoc}
455    */
456   public static function createFromItemType($item_type) {
457     // Forward to the field definition class for creating new data definitions
458     // via the typed manager.
459     return BaseFieldDefinition::createFromItemType($item_type);
460   }
461
462   /**
463    * {@inheritdoc}
464    */
465   public static function createFromDataType($type) {
466     // Forward to the field definition class for creating new data definitions
467     // via the typed manager.
468     return BaseFieldDefinition::createFromDataType($type);
469   }
470
471   /**
472    * {@inheritdoc}
473    */
474   public function getDataType() {
475     return 'list';
476   }
477
478   /**
479    * {@inheritdoc}
480    */
481   public function isList() {
482     return TRUE;
483   }
484
485   /**
486    * {@inheritdoc}
487    */
488   public function getClass() {
489     // Derive list class from the field type.
490     $type_definition = \Drupal::service('plugin.manager.field.field_type')
491       ->getDefinition($this->getType());
492     return $type_definition['list_class'];
493   }
494
495   /**
496    * {@inheritdoc}
497    */
498   public function getConstraints() {
499     return \Drupal::typedDataManager()->getDefaultConstraints($this) + $this->constraints;
500   }
501
502   /**
503    * {@inheritdoc}
504    */
505   public function getConstraint($constraint_name) {
506     $constraints = $this->getConstraints();
507     return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
508   }
509
510   /**
511    * {@inheritdoc}
512    */
513   public function getItemDefinition() {
514     if (!isset($this->itemDefinition)) {
515       $this->itemDefinition = FieldItemDataDefinition::create($this)
516         ->setSettings($this->getSettings());
517
518       // Add any custom property constraints, overwriting as required.
519       $item_constraints = $this->itemDefinition->getConstraint('ComplexData') ?: [];
520       foreach ($this->propertyConstraints as $name => $constraints) {
521         if (isset($item_constraints[$name])) {
522           $item_constraints[$name] = $constraints + $item_constraints[$name];
523         }
524         else {
525           $item_constraints[$name] = $constraints;
526         }
527         $this->itemDefinition->addConstraint('ComplexData', $item_constraints);
528       }
529     }
530
531     return $this->itemDefinition;
532   }
533
534   /**
535    * {@inheritdoc}
536    */
537   public function getConfig($bundle) {
538     return $this;
539   }
540
541   /**
542    * {@inheritdoc}
543    */
544   public function setConstraints(array $constraints) {
545     $this->constraints = $constraints;
546     return $this;
547   }
548
549   /**
550    * {@inheritdoc}
551    */
552   public function addConstraint($constraint_name, $options = NULL) {
553     $this->constraints[$constraint_name] = $options;
554     return $this;
555   }
556
557   /**
558    * {@inheritdoc}
559    */
560   public function setPropertyConstraints($name, array $constraints) {
561     $this->propertyConstraints[$name] = $constraints;
562
563     // Reset the field item definition so the next time it is instantiated it
564     // will receive the new constraints.
565     $this->itemDefinition = NULL;
566
567     return $this;
568   }
569
570   /**
571    * {@inheritdoc}
572    */
573   public function addPropertyConstraints($name, array $constraints) {
574     foreach ($constraints as $constraint_name => $options) {
575       $this->propertyConstraints[$name][$constraint_name] = $options;
576     }
577
578     // Reset the field item definition so the next time it is instantiated it
579     // will receive the new constraints.
580     $this->itemDefinition = NULL;
581
582     return $this;
583   }
584
585   /**
586    * {@inheritdoc}
587    */
588   public function isInternal() {
589     // Respect the definition, otherwise default to TRUE for computed fields.
590     if (isset($this->definition['internal'])) {
591       return $this->definition['internal'];
592     }
593     return $this->isComputed();
594   }
595
596 }