1<?php 2 3namespace Drupal\Core\Field\Entity; 4 5use Drupal\Core\Entity\EntityStorageInterface; 6use Drupal\Core\Field\BaseFieldDefinition; 7use Drupal\Core\Field\FieldConfigBase; 8use Drupal\Core\Field\FieldException; 9 10/** 11 * Defines the base field override entity. 12 * 13 * Allows base fields to be overridden on the bundle level. 14 * 15 * @ConfigEntityType( 16 * id = "base_field_override", 17 * label = @Translation("Base field override"), 18 * handlers = { 19 * "storage" = "Drupal\Core\Field\BaseFieldOverrideStorage", 20 * "access" = "Drupal\Core\Field\BaseFieldOverrideAccessControlHandler", 21 * }, 22 * config_prefix = "base_field_override", 23 * entity_keys = { 24 * "id" = "id", 25 * "label" = "label" 26 * }, 27 * config_export = { 28 * "id", 29 * "field_name", 30 * "entity_type", 31 * "bundle", 32 * "label", 33 * "description", 34 * "required", 35 * "translatable", 36 * "default_value", 37 * "default_value_callback", 38 * "settings", 39 * "field_type", 40 * } 41 * ) 42 */ 43class BaseFieldOverride extends FieldConfigBase { 44 45 /** 46 * The base field definition. 47 * 48 * @var \Drupal\Core\Field\BaseFieldDefinition 49 */ 50 protected $baseFieldDefinition; 51 52 /** 53 * Creates a base field override object. 54 * 55 * @param \Drupal\Core\Field\BaseFieldDefinition $base_field_definition 56 * The base field definition to override. 57 * @param string $bundle 58 * The bundle to which the override applies. 59 * 60 * @return \Drupal\Core\Field\Entity\BaseFieldOverride 61 * A new base field override object. 62 */ 63 public static function createFromBaseFieldDefinition(BaseFieldDefinition $base_field_definition, $bundle) { 64 $values = $base_field_definition->toArray(); 65 $values['bundle'] = $bundle; 66 $values['baseFieldDefinition'] = $base_field_definition; 67 return \Drupal::entityTypeManager()->getStorage('base_field_override')->create($values); 68 } 69 70 /** 71 * Constructs a BaseFieldOverride object. 72 * 73 * In most cases, base field override entities are created via 74 * BaseFieldOverride::createFromBaseFieldDefinition($definition, 'bundle') 75 * 76 * @param array $values 77 * An array of base field bundle override properties, keyed by property 78 * name. The field to override is specified by referring to an existing 79 * field with: 80 * - field_name: The field name. 81 * - entity_type: The entity type. 82 * Additionally, a 'bundle' property is required to indicate the entity 83 * bundle to which the bundle field override is attached to. Other array 84 * elements will be used to set the corresponding properties on the class; 85 * see the class property documentation for details. 86 * @param string $entity_type 87 * (optional) The type of the entity to create. Defaults to 88 * 'base_field_override'. 89 * 90 * @throws \Drupal\Core\Field\FieldException 91 * Exception thrown if $values does not contain a field_name, entity_type or 92 * bundle value. 93 */ 94 public function __construct(array $values, $entity_type = 'base_field_override') { 95 if (empty($values['field_name'])) { 96 throw new FieldException('Attempt to create a base field bundle override of a field without a field_name'); 97 } 98 if (empty($values['entity_type'])) { 99 throw new FieldException("Attempt to create a base field bundle override of field {$values['field_name']} without an entity_type"); 100 } 101 if (empty($values['bundle'])) { 102 throw new FieldException("Attempt to create a base field bundle override of field {$values['field_name']} without a bundle"); 103 } 104 105 parent::__construct($values, $entity_type); 106 } 107 108 /** 109 * {@inheritdoc} 110 */ 111 public function getFieldStorageDefinition() { 112 return $this->getBaseFieldDefinition()->getFieldStorageDefinition(); 113 } 114 115 /** 116 * {@inheritdoc} 117 */ 118 public function isDisplayConfigurable($context) { 119 return $this->getBaseFieldDefinition()->isDisplayConfigurable($context); 120 } 121 122 /** 123 * {@inheritdoc} 124 */ 125 public function getDisplayOptions($display_context) { 126 return $this->getBaseFieldDefinition()->getDisplayOptions($display_context); 127 } 128 129 /** 130 * {@inheritdoc} 131 */ 132 public function isReadOnly() { 133 return $this->getBaseFieldDefinition()->isReadOnly(); 134 } 135 136 /** 137 * {@inheritdoc} 138 */ 139 public function isComputed() { 140 return $this->getBaseFieldDefinition()->isComputed(); 141 } 142 143 /** 144 * {@inheritdoc} 145 */ 146 public function getClass() { 147 return $this->getBaseFieldDefinition()->getClass(); 148 } 149 150 /** 151 * {@inheritdoc} 152 */ 153 public function getUniqueIdentifier() { 154 return $this->getBaseFieldDefinition()->getUniqueIdentifier(); 155 } 156 157 /** 158 * Gets the base field definition. 159 * 160 * @return \Drupal\Core\Field\BaseFieldDefinition 161 */ 162 protected function getBaseFieldDefinition() { 163 if (!isset($this->baseFieldDefinition)) { 164 $fields = \Drupal::service('entity_field.manager')->getBaseFieldDefinitions($this->entity_type); 165 $this->baseFieldDefinition = $fields[$this->getName()]; 166 } 167 return $this->baseFieldDefinition; 168 } 169 170 /** 171 * {@inheritdoc} 172 * 173 * @throws \Drupal\Core\Field\FieldException 174 * If the bundle is being changed. 175 */ 176 public function preSave(EntityStorageInterface $storage) { 177 // Filter out unknown settings and make sure all settings are present, so 178 // that a complete field definition is passed to the various hooks and 179 // written to config. 180 $field_type_manager = \Drupal::service('plugin.manager.field.field_type'); 181 $default_settings = $field_type_manager->getDefaultFieldSettings($this->getType()); 182 $this->settings = array_intersect_key($this->settings, $default_settings) + $default_settings; 183 184 // Call the parent's presave method to perform validate and calculate 185 // dependencies. 186 parent::preSave($storage); 187 188 if ($this->isNew()) { 189 // @todo This assumes that the previous definition isn't some 190 // non-config-based override, but that might not be the case: 191 // https://www.drupal.org/node/2321071. 192 $previous_definition = $this->getBaseFieldDefinition(); 193 } 194 else { 195 // Some updates are always disallowed. 196 if ($this->entity_type != $this->original->entity_type) { 197 throw new FieldException("Cannot change the entity_type of an existing base field bundle override (entity type:{$this->entity_type}, bundle:{$this->original->bundle}, field name: {$this->field_name})"); 198 } 199 if ($this->bundle != $this->original->bundle) { 200 throw new FieldException("Cannot change the bundle of an existing base field bundle override (entity type:{$this->entity_type}, bundle:{$this->original->bundle}, field name: {$this->field_name})"); 201 } 202 $previous_definition = $this->original; 203 } 204 // Notify the entity storage. 205 $this->entityTypeManager()->getStorage($this->getTargetEntityTypeId())->onFieldDefinitionUpdate($this, $previous_definition); 206 } 207 208 /** 209 * {@inheritdoc} 210 */ 211 public static function postDelete(EntityStorageInterface $storage, array $field_overrides) { 212 $entity_type_manager = \Drupal::entityTypeManager(); 213 // Clear the cache upfront, to refresh the results of getBundles(). 214 \Drupal::service('entity_field.manager')->clearCachedFieldDefinitions(); 215 /** @var \Drupal\Core\Field\Entity\BaseFieldOverride $field_override */ 216 foreach ($field_overrides as $field_override) { 217 // Inform the system that the field definition is being updated back to 218 // its non-overridden state. 219 // @todo This assumes that there isn't a non-config-based override that 220 // we're returning to, but that might not be the case: 221 // https://www.drupal.org/node/2321071. 222 $entity_type_manager->getStorage($field_override->getTargetEntityTypeId())->onFieldDefinitionUpdate($field_override->getBaseFieldDefinition(), $field_override); 223 } 224 } 225 226 /** 227 * Loads a base field bundle override config entity. 228 * 229 * @param string $entity_type_id 230 * ID of the entity type. 231 * @param string $bundle 232 * Bundle name. 233 * @param string $field_name 234 * Name of the field. 235 * 236 * @return \Drupal\Core\Field\FieldConfigInterface|null 237 * The base field bundle override config entity if one exists for the 238 * provided field name, otherwise NULL. 239 */ 240 public static function loadByName($entity_type_id, $bundle, $field_name) { 241 return \Drupal::entityTypeManager()->getStorage('base_field_override')->load($entity_type_id . '.' . $bundle . '.' . $field_name); 242 } 243 244 /** 245 * Implements the magic __sleep() method. 246 */ 247 public function __sleep() { 248 // Only serialize necessary properties, excluding those that can be 249 // recalculated. 250 unset($this->baseFieldDefinition); 251 return parent::__sleep(); 252 } 253 254} 255