3 namespace Drupal\Core\Field;
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;
11 * Base class for configurable field definitions.
13 abstract class FieldConfigBase extends ConfigEntityBase implements FieldConfigInterface {
15 use FieldInputValueNormalizerTrait;
20 * The ID consists of 3 parts: the entity type, bundle and the field name.
22 * Example: node.article.body, user.user.field_main_image.
33 protected $field_name;
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
46 protected $field_type;
49 * The name of the entity type the field is attached to.
53 protected $entity_type;
56 * The name of the bundle the field is attached to.
63 * The human-readable label for the field.
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.
68 * If not specified, this defaults to the field_name (mostly useful for fields
76 * The field description.
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.
84 protected $description = '';
87 * Field-type specific settings.
89 * An array of key/value pairs. The keys and default values are defined by the
94 protected $settings = [];
97 * Flag indicating whether the field is required.
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.
105 protected $required = FALSE;
108 * Flag indicating whether the field is translatable.
114 protected $translatable = TRUE;
117 * Default field value.
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.
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.
132 * This property is overlooked if the $default_value_callback is non-empty.
134 * Example for a integer field:
137 * array('value' => 1),
138 * array('value' => 2),
144 protected $default_value = [];
147 * The name of a callback function that returns default values.
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.
157 * This property takes precedence on the list of fixed values specified in the
158 * $default_value property.
162 protected $default_value_callback = '';
165 * The field storage object.
167 * @var \Drupal\Core\Field\FieldStorageDefinitionInterface
169 protected $fieldStorage;
172 * The data definition of a field item.
174 * @var \Drupal\Core\Field\TypedData\FieldItemDataDefinition
176 protected $itemDefinition;
179 * Array of constraint options keyed by constraint plugin ID.
183 protected $constraints = [];
186 * Array of property constraint options keyed by property ID. The values are
187 * associative array of constraint options keyed by constraint plugin ID.
191 protected $propertyConstraints = [];
196 public function id() {
197 return $this->entity_type . '.' . $this->bundle . '.' . $this->field_name;
203 public function getName() {
204 return $this->field_name;
210 public function getType() {
211 return $this->field_type;
217 public function getTargetEntityTypeId() {
218 return $this->entity_type;
224 public function getTargetBundle() {
225 return $this->bundle;
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']);
244 // Let the field type plugin specify its own dependencies.
245 // @see \Drupal\Core\Field\FieldItemInterface::calculateDependencies()
246 $this->addDependencies($definition['class']::calculateDependencies($this));
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']);
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)) {
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();
284 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
286 $this->entityManager()->clearCachedFieldDefinitions();
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();
298 public function getLabel() {
299 return $this->label();
305 public function setLabel($label) {
306 $this->label = $label;
313 public function getDescription() {
314 return $this->description;
320 public function setDescription($description) {
321 $this->description = $description;
328 public function isTranslatable() {
329 // A field can be enabled for translation only if translation is supported.
330 return $this->translatable && $this->getFieldStorageDefinition()->isTranslatable();
336 public function setTranslatable($translatable) {
337 $this->translatable = $translatable;
344 public function getSettings() {
345 return $this->settings + $this->getFieldStorageDefinition()->getSettings();
351 public function setSettings(array $settings) {
352 $this->settings = $settings + $this->settings;
359 public function getSetting($setting_name) {
360 if (array_key_exists($setting_name, $this->settings)) {
361 return $this->settings[$setting_name];
364 return $this->getFieldStorageDefinition()->getSetting($setting_name);
371 public function setSetting($setting_name, $value) {
372 $this->settings[$setting_name] = $value;
379 public function isRequired() {
380 return $this->required;
386 public function setRequired($required) {
387 $this->required = $required;
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());
401 $value = $this->getDefaultValueLiteral();
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);
411 public function getDefaultValueLiteral() {
412 return $this->default_value;
418 public function setDefaultValue($value) {
419 $this->default_value = $this->normalizeValue($value, $this->getFieldStorageDefinition()->getMainPropertyName());
426 public function getDefaultValueCallback() {
427 return $this->default_value_callback;
433 public function setDefaultValueCallback($callback) {
434 $this->default_value_callback = $callback;
439 * Implements the magic __sleep() method.
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.
445 public function __sleep() {
446 // Only serialize necessary properties, excluding those that can be
448 $properties = get_object_vars($this);
449 unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['original']);
450 return array_keys($properties);
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);
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);
474 public function getDataType() {
481 public function isList() {
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'];
498 public function getConstraints() {
499 return \Drupal::typedDataManager()->getDefaultConstraints($this) + $this->constraints;
505 public function getConstraint($constraint_name) {
506 $constraints = $this->getConstraints();
507 return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
513 public function getItemDefinition() {
514 if (!isset($this->itemDefinition)) {
515 $this->itemDefinition = FieldItemDataDefinition::create($this)
516 ->setSettings($this->getSettings());
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];
525 $item_constraints[$name] = $constraints;
527 $this->itemDefinition->addConstraint('ComplexData', $item_constraints);
531 return $this->itemDefinition;
537 public function getConfig($bundle) {
544 public function setConstraints(array $constraints) {
545 $this->constraints = $constraints;
552 public function addConstraint($constraint_name, $options = NULL) {
553 $this->constraints[$constraint_name] = $options;
560 public function setPropertyConstraints($name, array $constraints) {
561 $this->propertyConstraints[$name] = $constraints;
563 // Reset the field item definition so the next time it is instantiated it
564 // will receive the new constraints.
565 $this->itemDefinition = NULL;
573 public function addPropertyConstraints($name, array $constraints) {
574 foreach ($constraints as $constraint_name => $options) {
575 $this->propertyConstraints[$name][$constraint_name] = $options;
578 // Reset the field item definition so the next time it is instantiated it
579 // will receive the new constraints.
580 $this->itemDefinition = NULL;
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'];
593 return $this->isComputed();