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 {
18 * The ID consists of 3 parts: the entity type, bundle and the field name.
20 * Example: node.article.body, user.user.field_main_image.
31 protected $field_name;
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
44 protected $field_type;
47 * The name of the entity type the field is attached to.
51 protected $entity_type;
54 * The name of the bundle the field is attached to.
61 * The human-readable label for the field.
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.
66 * If not specified, this defaults to the field_name (mostly useful for fields
74 * The field description.
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.
82 protected $description = '';
85 * Field-type specific settings.
87 * An array of key/value pairs. The keys and default values are defined by the
92 protected $settings = [];
95 * Flag indicating whether the field is required.
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.
103 protected $required = FALSE;
106 * Flag indicating whether the field is translatable.
112 protected $translatable = TRUE;
115 * Default field value.
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.
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.
130 * This property is overlooked if the $default_value_callback is non-empty.
132 * Example for a integer field:
135 * array('value' => 1),
136 * array('value' => 2),
142 protected $default_value = [];
145 * The name of a callback function that returns default values.
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.
155 * This property takes precedence on the list of fixed values specified in the
156 * $default_value property.
160 protected $default_value_callback = '';
163 * The field storage object.
165 * @var \Drupal\Core\Field\FieldStorageDefinitionInterface
167 protected $fieldStorage;
170 * The data definition of a field item.
172 * @var \Drupal\Core\Field\TypedData\FieldItemDataDefinition
174 protected $itemDefinition;
177 * Array of constraint options keyed by constraint plugin ID.
181 protected $constraints = [];
184 * Array of property constraint options keyed by property ID. The values are
185 * associative array of constraint options keyed by constraint plugin ID.
189 protected $propertyConstraints = [];
194 public function id() {
195 return $this->entity_type . '.' . $this->bundle . '.' . $this->field_name;
201 public function getName() {
202 return $this->field_name;
208 public function getType() {
209 return $this->field_type;
215 public function getTargetEntityTypeId() {
216 return $this->entity_type;
222 public function getTargetBundle() {
223 return $this->bundle;
229 public function calculateDependencies() {
230 parent::calculateDependencies();
231 // Add dependencies from the field type plugin. We can not use
232 // self::calculatePluginDependencies() because instantiation of a field item
233 // plugin requires a parent entity.
234 /** @var $field_type_manager \Drupal\Core\Field\FieldTypePluginManagerInterface */
235 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
236 $definition = $field_type_manager->getDefinition($this->getType());
237 $this->addDependency('module', $definition['provider']);
238 // Plugins can declare additional dependencies in their definition.
239 if (isset($definition['config_dependencies'])) {
240 $this->addDependencies($definition['config_dependencies']);
242 // Let the field type plugin specify its own dependencies.
243 // @see \Drupal\Core\Field\FieldItemInterface::calculateDependencies()
244 $this->addDependencies($definition['class']::calculateDependencies($this));
246 // Create dependency on the bundle.
247 $bundle_config_dependency = $this->entityManager()->getDefinition($this->entity_type)->getBundleConfigDependency($this->bundle);
248 $this->addDependency($bundle_config_dependency['type'], $bundle_config_dependency['name']);
256 public function onDependencyRemoval(array $dependencies) {
257 $changed = parent::onDependencyRemoval($dependencies);
258 $field_type_manager = \Drupal::service('plugin.manager.field.field_type');
259 $definition = $field_type_manager->getDefinition($this->getType());
260 if ($definition['class']::onDependencyRemoval($this, $dependencies)) {
270 public function postCreate(EntityStorageInterface $storage) {
271 parent::postCreate($storage);
272 // If it was not present in the $values passed to create(), (e.g. for
273 // programmatic creation), populate the denormalized field_type property
274 // from the field storage, so that it gets saved in the config record.
275 if (empty($this->field_type)) {
276 $this->field_type = $this->getFieldStorageDefinition()->getType();
283 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
285 $this->entityManager()->clearCachedFieldDefinitions();
287 // Invalidate the render cache for all affected entities.
288 $entity_type = $this->getFieldStorageDefinition()->getTargetEntityTypeId();
289 if ($this->entityManager()->hasHandler($entity_type, 'view_builder')) {
290 $this->entityManager()->getViewBuilder($entity_type)->resetCache();
297 public function getLabel() {
298 return $this->label();
304 public function setLabel($label) {
305 $this->label = $label;
312 public function getDescription() {
313 return $this->description;
319 public function setDescription($description) {
320 $this->description = $description;
327 public function isTranslatable() {
328 // A field can be enabled for translation only if translation is supported.
329 return $this->translatable && $this->getFieldStorageDefinition()->isTranslatable();
335 public function setTranslatable($translatable) {
336 $this->translatable = $translatable;
343 public function getSettings() {
344 return $this->settings + $this->getFieldStorageDefinition()->getSettings();
350 public function setSettings(array $settings) {
351 $this->settings = $settings + $this->settings;
358 public function getSetting($setting_name) {
359 if (array_key_exists($setting_name, $this->settings)) {
360 return $this->settings[$setting_name];
363 return $this->getFieldStorageDefinition()->getSetting($setting_name);
370 public function setSetting($setting_name, $value) {
371 $this->settings[$setting_name] = $value;
378 public function isRequired() {
379 return $this->required;
385 public function setRequired($required) {
386 $this->required = $required;
393 public function getDefaultValue(FieldableEntityInterface $entity) {
394 // Allow custom default values function.
395 if ($callback = $this->getDefaultValueCallback()) {
396 $value = call_user_func($callback, $entity, $this);
399 $value = $this->getDefaultValueLiteral();
401 // Allow the field type to process default values.
402 $field_item_list_class = $this->getClass();
403 return $field_item_list_class::processDefaultValue($value, $entity, $this);
409 public function getDefaultValueLiteral() {
410 return $this->default_value;
416 public function setDefaultValue($value) {
417 if (!is_array($value)) {
418 if ($value === NULL) {
421 $key = $this->getFieldStorageDefinition()->getPropertyNames()[0];
422 // Convert to the multi value format to support fields with a cardinality
428 $this->default_value = $value;
435 public function getDefaultValueCallback() {
436 return $this->default_value_callback;
442 public function setDefaultValueCallback($callback) {
443 $this->default_value_callback = $callback;
448 * Implements the magic __sleep() method.
450 * Using the Serialize interface and serialize() / unserialize() methods
451 * breaks entity forms in PHP 5.4.
452 * @todo Investigate in https://www.drupal.org/node/2074253.
454 public function __sleep() {
455 // Only serialize necessary properties, excluding those that can be
457 $properties = get_object_vars($this);
458 unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['original']);
459 return array_keys($properties);
465 public static function createFromItemType($item_type) {
466 // Forward to the field definition class for creating new data definitions
467 // via the typed manager.
468 return BaseFieldDefinition::createFromItemType($item_type);
474 public static function createFromDataType($type) {
475 // Forward to the field definition class for creating new data definitions
476 // via the typed manager.
477 return BaseFieldDefinition::createFromDataType($type);
483 public function getDataType() {
490 public function isList() {
497 public function getClass() {
498 // Derive list class from the field type.
499 $type_definition = \Drupal::service('plugin.manager.field.field_type')
500 ->getDefinition($this->getType());
501 return $type_definition['list_class'];
507 public function getConstraints() {
508 return \Drupal::typedDataManager()->getDefaultConstraints($this) + $this->constraints;
514 public function getConstraint($constraint_name) {
515 $constraints = $this->getConstraints();
516 return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
522 public function getItemDefinition() {
523 if (!isset($this->itemDefinition)) {
524 $this->itemDefinition = FieldItemDataDefinition::create($this)
525 ->setSettings($this->getSettings());
527 // Add any custom property constraints, overwriting as required.
528 $item_constraints = $this->itemDefinition->getConstraint('ComplexData') ?: [];
529 foreach ($this->propertyConstraints as $name => $constraints) {
530 if (isset($item_constraints[$name])) {
531 $item_constraints[$name] = $constraints + $item_constraints[$name];
534 $item_constraints[$name] = $constraints;
536 $this->itemDefinition->addConstraint('ComplexData', $item_constraints);
540 return $this->itemDefinition;
546 public function getConfig($bundle) {
553 public function setConstraints(array $constraints) {
554 $this->constraints = $constraints;
561 public function addConstraint($constraint_name, $options = NULL) {
562 $this->constraints[$constraint_name] = $options;
569 public function setPropertyConstraints($name, array $constraints) {
570 $this->propertyConstraints[$name] = $constraints;
572 // Reset the field item definition so the next time it is instantiated it
573 // will receive the new constraints.
574 $this->itemDefinition = NULL;
582 public function addPropertyConstraints($name, array $constraints) {
583 foreach ($constraints as $constraint_name => $options) {
584 $this->propertyConstraints[$name][$constraint_name] = $options;
587 // Reset the field item definition so the next time it is instantiated it
588 // will receive the new constraints.
589 $this->itemDefinition = NULL;