save()); the default value is * added if the $entity object provides no explicit entry (actual values or * "the field is empty") for the field. * * The default value is expressed as a numerically indexed array of items, * each item being an array of key/value pairs matching the set of 'columns' * defined by the "field schema" for the field type, as exposed in the class * implementing \Drupal\Core\Field\FieldItemInterface::schema() method. If the * number of items exceeds the cardinality of the field, extraneous items will * be ignored. * * This property is overlooked if the $default_value_callback is non-empty. * * Example for an integer field: * @code * array( * array('value' => 1), * array('value' => 2), * ) * @endcode * * @var array */ protected $default_value = []; /** * The name of a callback function that returns default values. * * The function will be called with the following arguments: * - \Drupal\Core\Entity\FieldableEntityInterface $entity * The entity being created. * - \Drupal\Core\Field\FieldDefinitionInterface $definition * The field definition. * It should return an array of default values, in the same format as the * $default_value property. * * This property takes precedence on the list of fixed values specified in the * $default_value property. * * @var string */ protected $default_value_callback = ''; /** * The field storage object. * * @var \Drupal\Core\Field\FieldStorageDefinitionInterface */ protected $fieldStorage; /** * The data definition of a field item. * * @var \Drupal\Core\Field\TypedData\FieldItemDataDefinition */ protected $itemDefinition; /** * Array of constraint options keyed by constraint plugin ID. * * @var array */ protected $constraints = []; /** * Array of property constraint options keyed by property ID. * * The values are associative array of constraint options keyed by constraint * plugin ID. * * @var array[] */ protected $propertyConstraints = []; /** * {@inheritdoc} */ public function id() { return $this->entity_type . '.' . $this->bundle . '.' . $this->field_name; } /** * {@inheritdoc} */ public function getName() { return $this->field_name; } /** * {@inheritdoc} */ public function getType() { return $this->field_type; } /** * {@inheritdoc} */ public function getTargetEntityTypeId() { return $this->entity_type; } /** * {@inheritdoc} */ public function getTargetBundle() { return $this->bundle; } /** * {@inheritdoc} */ public function calculateDependencies() { parent::calculateDependencies(); // Add dependencies from the field type plugin. We can not use // self::calculatePluginDependencies() because instantiation of a field item // plugin requires a parent entity. /** @var \Drupal\Core\Field\FieldTypePluginManagerInterface $field_type_manager */ $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); $definition = $field_type_manager->getDefinition($this->getType()); $this->addDependency('module', $definition['provider']); // Plugins can declare additional dependencies in their definition. if (isset($definition['config_dependencies'])) { $this->addDependencies($definition['config_dependencies']); } // Let the field type plugin specify its own dependencies. // @see \Drupal\Core\Field\FieldItemInterface::calculateDependencies() $this->addDependencies($definition['class']::calculateDependencies($this)); // Create dependency on the bundle. $bundle_config_dependency = $this->entityTypeManager()->getDefinition($this->entity_type)->getBundleConfigDependency($this->bundle); $this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']); return $this; } /** * {@inheritdoc} */ public function onDependencyRemoval(array $dependencies) { $changed = parent::onDependencyRemoval($dependencies); $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); $definition = $field_type_manager->getDefinition($this->getType()); if ($definition['class']::onDependencyRemoval($this, $dependencies)) { $changed = TRUE; } return $changed; } /** * {@inheritdoc} */ public function postCreate(EntityStorageInterface $storage) { parent::postCreate($storage); // If it was not present in the $values passed to create(), (e.g. for // programmatic creation), populate the denormalized field_type property // from the field storage, so that it gets saved in the config record. if (empty($this->field_type)) { $this->field_type = $this->getFieldStorageDefinition()->getType(); } // Make sure all expected runtime settings are present. $default_settings = \Drupal::service('plugin.manager.field.field_type') ->getDefaultFieldSettings($this->getType()); // Filter out any unknown (unsupported) settings. $supported_settings = array_intersect_key($this->getSettings(), $default_settings); $this->set('settings', $supported_settings + $default_settings); } /** * {@inheritdoc} */ public static function postDelete(EntityStorageInterface $storage, array $fields) { // Clear the cache upfront, to refresh the results of getBundles(). \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); // Notify the entity storage. foreach ($fields as $field) { if (!$field->deleted) { \Drupal::service('field_definition.listener')->onFieldDefinitionDelete($field); } } } /** * {@inheritdoc} */ public function postSave(EntityStorageInterface $storage, $update = TRUE) { // Clear the cache. \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); // Invalidate the render cache for all affected entities. $entity_type = $this->getFieldStorageDefinition()->getTargetEntityTypeId(); if ($this->entityTypeManager()->hasHandler($entity_type, 'view_builder')) { $this->entityTypeManager()->getViewBuilder($entity_type)->resetCache(); } } /** * {@inheritdoc} */ public function getLabel() { return $this->label(); } /** * {@inheritdoc} */ public function setLabel($label) { $this->label = $label; return $this; } /** * {@inheritdoc} */ public function getDescription() { return $this->description; } /** * {@inheritdoc} */ public function setDescription($description) { $this->description = $description; return $this; } /** * {@inheritdoc} */ public function isTranslatable() { // A field can be enabled for translation only if translation is supported. return $this->translatable && $this->getFieldStorageDefinition()->isTranslatable(); } /** * {@inheritdoc} */ public function setTranslatable($translatable) { $this->translatable = $translatable; return $this; } /** * {@inheritdoc} */ public function getSettings() { return $this->settings + $this->getFieldStorageDefinition()->getSettings(); } /** * {@inheritdoc} */ public function setSettings(array $settings) { $this->settings = $settings + $this->settings; return $this; } /** * {@inheritdoc} */ public function getSetting($setting_name) { if (array_key_exists($setting_name, $this->settings)) { return $this->settings[$setting_name]; } else { return $this->getFieldStorageDefinition()->getSetting($setting_name); } } /** * {@inheritdoc} */ public function setSetting($setting_name, $value) { $this->settings[$setting_name] = $value; return $this; } /** * {@inheritdoc} */ public function isRequired() { return $this->required; } /** * {@inheritdoc} */ public function setRequired($required) { $this->required = $required; return $this; } /** * {@inheritdoc} */ public function getDefaultValue(FieldableEntityInterface $entity) { // Allow custom default values function. if ($callback = $this->getDefaultValueCallback()) { $value = call_user_func($callback, $entity, $this); $value = $this->normalizeValue($value, $this->getFieldStorageDefinition()->getMainPropertyName()); } else { $value = $this->getDefaultValueLiteral(); } // Allow the field type to process default values. $field_item_list_class = $this->getClass(); return $field_item_list_class::processDefaultValue($value, $entity, $this); } /** * {@inheritdoc} */ public function getDefaultValueLiteral() { return $this->default_value; } /** * {@inheritdoc} */ public function setDefaultValue($value) { $this->default_value = $this->normalizeValue($value, $this->getFieldStorageDefinition()->getMainPropertyName()); return $this; } /** * {@inheritdoc} */ public function getDefaultValueCallback() { return $this->default_value_callback; } /** * {@inheritdoc} */ public function setDefaultValueCallback($callback) { $this->default_value_callback = $callback; return $this; } /** * Implements the magic __sleep() method. * * Using the Serialize interface and serialize() / unserialize() methods * breaks entity forms in PHP 5.4. * @todo Investigate in https://www.drupal.org/node/1977206. */ public function __sleep(): array { $properties = get_object_vars($this); // Only serialize necessary properties, excluding those that can be // recalculated. unset($properties['itemDefinition'], $properties['original']); return array_keys($properties); } /** * {@inheritdoc} */ public static function createFromItemType($item_type) { // Forward to the field definition class for creating new data definitions // via the typed manager. return BaseFieldDefinition::createFromItemType($item_type); } /** * {@inheritdoc} */ public static function createFromDataType($type) { // Forward to the field definition class for creating new data definitions // via the typed manager. return BaseFieldDefinition::createFromDataType($type); } /** * {@inheritdoc} */ public function getDataType() { // This object serves as data definition for field item lists, thus // the correct data type is 'list'. This is not to be confused with // the config schema type, 'field_config_base', which is used to // describe the schema of the configuration backing this objects. // @see \Drupal\Core\Field\FieldItemList // @see \Drupal\Core\TypedData\DataDefinitionInterface return 'list'; } /** * {@inheritdoc} */ public function isList() { return TRUE; } /** * {@inheritdoc} */ public function getClass() { // Derive list class from the field type. $type_definition = \Drupal::service('plugin.manager.field.field_type') ->getDefinition($this->getType()); return $type_definition['list_class']; } /** * {@inheritdoc} */ public function getConstraints() { return \Drupal::typedDataManager()->getDefaultConstraints($this) + $this->constraints; } /** * {@inheritdoc} */ public function getConstraint($constraint_name) { $constraints = $this->getConstraints(); return $constraints[$constraint_name] ?? NULL; } /** * {@inheritdoc} */ public function getItemDefinition() { if (!isset($this->itemDefinition)) { $this->itemDefinition = FieldItemDataDefinition::create($this) ->setSettings($this->getSettings()); // Add any custom property constraints, overwriting as required. $item_constraints = $this->itemDefinition->getConstraint('ComplexData') ?: []; foreach ($this->propertyConstraints as $name => $constraints) { if (isset($item_constraints[$name])) { $item_constraints[$name] = $constraints + $item_constraints[$name]; } else { $item_constraints[$name] = $constraints; } $this->itemDefinition->addConstraint('ComplexData', $item_constraints); } } return $this->itemDefinition; } /** * {@inheritdoc} */ public function getConfig($bundle) { return $this; } /** * {@inheritdoc} */ public function setConstraints(array $constraints) { $this->constraints = $constraints; return $this; } /** * {@inheritdoc} */ public function addConstraint($constraint_name, $options = NULL) { $this->constraints[$constraint_name] = $options; return $this; } /** * {@inheritdoc} */ public function setPropertyConstraints($name, array $constraints) { $this->propertyConstraints[$name] = $constraints; // Reset the field item definition so the next time it is instantiated it // will receive the new constraints. $this->itemDefinition = NULL; return $this; } /** * {@inheritdoc} */ public function addPropertyConstraints($name, array $constraints) { foreach ($constraints as $constraint_name => $options) { $this->propertyConstraints[$name][$constraint_name] = $options; } // Reset the field item definition so the next time it is instantiated it // will receive the new constraints. $this->itemDefinition = NULL; return $this; } /** * {@inheritdoc} */ public function isInternal() { // Respect the definition, otherwise default to TRUE for computed fields. if (isset($this->definition['internal'])) { return $this->definition['internal']; } return $this->isComputed(); } }