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