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 = [];
186 public function id() {
187 return $this->entity_type . '.' . $this->bundle . '.' . $this->field_name;
193 public function getName() {
194 return $this->field_name;
200 public function getType() {
201 return $this->field_type;
207 public function getTargetEntityTypeId() {
208 return $this->entity_type;
214 public function getTargetBundle() {
215 return $this->bundle;
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']);
234 // Let the field type plugin specify its own dependencies.
235 // @see \Drupal\Core\Field\FieldItemInterface::calculateDependencies()
236 $this->addDependencies($definition['class']::calculateDependencies($this));
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']);
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)) {
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();
275 public function postSave(EntityStorageInterface $storage, $update = TRUE) {
277 $this->entityManager()->clearCachedFieldDefinitions();
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();
289 public function getLabel() {
290 return $this->label();
296 public function setLabel($label) {
297 $this->label = $label;
304 public function getDescription() {
305 return $this->description;
311 public function setDescription($description) {
312 $this->description = $description;
319 public function isTranslatable() {
320 // A field can be enabled for translation only if translation is supported.
321 return $this->translatable && $this->getFieldStorageDefinition()->isTranslatable();
327 public function setTranslatable($translatable) {
328 $this->translatable = $translatable;
335 public function getSettings() {
336 return $this->settings + $this->getFieldStorageDefinition()->getSettings();
342 public function setSettings(array $settings) {
343 $this->settings = $settings + $this->settings;
350 public function getSetting($setting_name) {
351 if (array_key_exists($setting_name, $this->settings)) {
352 return $this->settings[$setting_name];
355 return $this->getFieldStorageDefinition()->getSetting($setting_name);
362 public function setSetting($setting_name, $value) {
363 $this->settings[$setting_name] = $value;
370 public function isRequired() {
371 return $this->required;
377 public function setRequired($required) {
378 $this->required = $required;
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);
391 $value = $this->getDefaultValueLiteral();
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);
401 public function getDefaultValueLiteral() {
402 return $this->default_value;
408 public function setDefaultValue($value) {
409 if (!is_array($value)) {
410 if ($value === NULL) {
413 $key = $this->getFieldStorageDefinition()->getPropertyNames()[0];
414 // Convert to the multi value format to support fields with a cardinality
420 $this->default_value = $value;
427 public function getDefaultValueCallback() {
428 return $this->default_value_callback;
434 public function setDefaultValueCallback($callback) {
435 $this->default_value_callback = $callback;
440 * Implements the magic __sleep() method.
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.
446 public function __sleep() {
447 // Only serialize necessary properties, excluding those that can be
449 $properties = get_object_vars($this);
450 unset($properties['fieldStorage'], $properties['itemDefinition'], $properties['original']);
451 return array_keys($properties);
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);
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);
475 public function getDataType() {
482 public function isList() {
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'];
499 public function getConstraints() {
500 return \Drupal::typedDataManager()->getDefaultConstraints($this) + $this->constraints;
506 public function getConstraint($constraint_name) {
507 $constraints = $this->getConstraints();
508 return isset($constraints[$constraint_name]) ? $constraints[$constraint_name] : NULL;
514 public function getItemDefinition() {
515 if (!isset($this->itemDefinition)) {
516 $this->itemDefinition = FieldItemDataDefinition::create($this)
517 ->setSettings($this->getSettings());
519 return $this->itemDefinition;
525 public function getConfig($bundle) {
532 public function setConstraints(array $constraints) {
533 $this->constraints = $constraints;
540 public function addConstraint($constraint_name, $options = NULL) {
541 $this->constraints[$constraint_name] = $options;
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);
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];
565 $item_constraints[$name] = $constraints;
567 $this->getItemDefinition()->addConstraint('ComplexData', $item_constraints);