annotate core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php @ 0:4c8ae668cc8c

Initial import (non-working)
author Chris Cannam
date Wed, 29 Nov 2017 16:09:58 +0000
parents
children 1fec387a4317
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\Core\Entity;
Chris@0 4
Chris@0 5 use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface;
Chris@0 6 use Drupal\Core\Entity\Schema\EntityStorageSchemaInterface;
Chris@0 7 use Drupal\Core\Field\BaseFieldDefinition;
Chris@0 8 use Drupal\Core\Field\FieldStorageDefinitionInterface;
Chris@0 9 use Drupal\Core\StringTranslation\StringTranslationTrait;
Chris@0 10
Chris@0 11 /**
Chris@0 12 * Manages entity definition updates.
Chris@0 13 */
Chris@0 14 class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInterface {
Chris@0 15 use StringTranslationTrait;
Chris@0 16
Chris@0 17 /**
Chris@0 18 * The entity manager service.
Chris@0 19 *
Chris@0 20 * @var \Drupal\Core\Entity\EntityManagerInterface
Chris@0 21 */
Chris@0 22 protected $entityManager;
Chris@0 23
Chris@0 24 /**
Chris@0 25 * Constructs a new EntityDefinitionUpdateManager.
Chris@0 26 *
Chris@0 27 * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
Chris@0 28 * The entity manager.
Chris@0 29 */
Chris@0 30 public function __construct(EntityManagerInterface $entity_manager) {
Chris@0 31 $this->entityManager = $entity_manager;
Chris@0 32 }
Chris@0 33
Chris@0 34 /**
Chris@0 35 * {@inheritdoc}
Chris@0 36 */
Chris@0 37 public function needsUpdates() {
Chris@0 38 return (bool) $this->getChangeList();
Chris@0 39 }
Chris@0 40
Chris@0 41 /**
Chris@0 42 * {@inheritdoc}
Chris@0 43 */
Chris@0 44 public function getChangeSummary() {
Chris@0 45 $summary = [];
Chris@0 46
Chris@0 47 foreach ($this->getChangeList() as $entity_type_id => $change_list) {
Chris@0 48 // Process entity type definition changes.
Chris@0 49 if (!empty($change_list['entity_type'])) {
Chris@0 50 $entity_type = $this->entityManager->getDefinition($entity_type_id);
Chris@0 51
Chris@0 52 switch ($change_list['entity_type']) {
Chris@0 53 case static::DEFINITION_CREATED:
Chris@0 54 $summary[$entity_type_id][] = $this->t('The %entity_type entity type needs to be installed.', ['%entity_type' => $entity_type->getLabel()]);
Chris@0 55 break;
Chris@0 56
Chris@0 57 case static::DEFINITION_UPDATED:
Chris@0 58 $summary[$entity_type_id][] = $this->t('The %entity_type entity type needs to be updated.', ['%entity_type' => $entity_type->getLabel()]);
Chris@0 59 break;
Chris@0 60 }
Chris@0 61 }
Chris@0 62
Chris@0 63 // Process field storage definition changes.
Chris@0 64 if (!empty($change_list['field_storage_definitions'])) {
Chris@0 65 $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
Chris@0 66 $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
Chris@0 67
Chris@0 68 foreach ($change_list['field_storage_definitions'] as $field_name => $change) {
Chris@0 69 switch ($change) {
Chris@0 70 case static::DEFINITION_CREATED:
Chris@0 71 $summary[$entity_type_id][] = $this->t('The %field_name field needs to be installed.', ['%field_name' => $storage_definitions[$field_name]->getLabel()]);
Chris@0 72 break;
Chris@0 73
Chris@0 74 case static::DEFINITION_UPDATED:
Chris@0 75 $summary[$entity_type_id][] = $this->t('The %field_name field needs to be updated.', ['%field_name' => $storage_definitions[$field_name]->getLabel()]);
Chris@0 76 break;
Chris@0 77
Chris@0 78 case static::DEFINITION_DELETED:
Chris@0 79 $summary[$entity_type_id][] = $this->t('The %field_name field needs to be uninstalled.', ['%field_name' => $original_storage_definitions[$field_name]->getLabel()]);
Chris@0 80 break;
Chris@0 81 }
Chris@0 82 }
Chris@0 83 }
Chris@0 84 }
Chris@0 85
Chris@0 86 return $summary;
Chris@0 87 }
Chris@0 88
Chris@0 89 /**
Chris@0 90 * {@inheritdoc}
Chris@0 91 */
Chris@0 92 public function applyUpdates() {
Chris@0 93 $complete_change_list = $this->getChangeList();
Chris@0 94 if ($complete_change_list) {
Chris@0 95 // self::getChangeList() only disables the cache and does not invalidate.
Chris@0 96 // In case there are changes, explicitly invalidate caches.
Chris@0 97 $this->entityManager->clearCachedDefinitions();
Chris@0 98 }
Chris@0 99 foreach ($complete_change_list as $entity_type_id => $change_list) {
Chris@0 100 // Process entity type definition changes before storage definitions ones
Chris@0 101 // this is necessary when you change an entity type from non-revisionable
Chris@0 102 // to revisionable and at the same time add revisionable fields to the
Chris@0 103 // entity type.
Chris@0 104 if (!empty($change_list['entity_type'])) {
Chris@0 105 $this->doEntityUpdate($change_list['entity_type'], $entity_type_id);
Chris@0 106 }
Chris@0 107
Chris@0 108 // Process field storage definition changes.
Chris@0 109 if (!empty($change_list['field_storage_definitions'])) {
Chris@0 110 $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
Chris@0 111 $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
Chris@0 112
Chris@0 113 foreach ($change_list['field_storage_definitions'] as $field_name => $change) {
Chris@0 114 $storage_definition = isset($storage_definitions[$field_name]) ? $storage_definitions[$field_name] : NULL;
Chris@0 115 $original_storage_definition = isset($original_storage_definitions[$field_name]) ? $original_storage_definitions[$field_name] : NULL;
Chris@0 116 $this->doFieldUpdate($change, $storage_definition, $original_storage_definition);
Chris@0 117 }
Chris@0 118 }
Chris@0 119 }
Chris@0 120 }
Chris@0 121
Chris@0 122 /**
Chris@0 123 * {@inheritdoc}
Chris@0 124 */
Chris@0 125 public function getEntityType($entity_type_id) {
Chris@0 126 $entity_type = $this->entityManager->getLastInstalledDefinition($entity_type_id);
Chris@0 127 return $entity_type ? clone $entity_type : NULL;
Chris@0 128 }
Chris@0 129
Chris@0 130 /**
Chris@0 131 * {@inheritdoc}
Chris@0 132 */
Chris@0 133 public function installEntityType(EntityTypeInterface $entity_type) {
Chris@0 134 $this->entityManager->clearCachedDefinitions();
Chris@0 135 $this->entityManager->onEntityTypeCreate($entity_type);
Chris@0 136 }
Chris@0 137
Chris@0 138 /**
Chris@0 139 * {@inheritdoc}
Chris@0 140 */
Chris@0 141 public function updateEntityType(EntityTypeInterface $entity_type) {
Chris@0 142 $original = $this->getEntityType($entity_type->id());
Chris@0 143 $this->entityManager->clearCachedDefinitions();
Chris@0 144 $this->entityManager->onEntityTypeUpdate($entity_type, $original);
Chris@0 145 }
Chris@0 146
Chris@0 147 /**
Chris@0 148 * {@inheritdoc}
Chris@0 149 */
Chris@0 150 public function uninstallEntityType(EntityTypeInterface $entity_type) {
Chris@0 151 $this->entityManager->clearCachedDefinitions();
Chris@0 152 $this->entityManager->onEntityTypeDelete($entity_type);
Chris@0 153 }
Chris@0 154
Chris@0 155 /**
Chris@0 156 * {@inheritdoc}
Chris@0 157 */
Chris@0 158 public function installFieldStorageDefinition($name, $entity_type_id, $provider, FieldStorageDefinitionInterface $storage_definition) {
Chris@0 159 // @todo Pass a mutable field definition interface when we have one. See
Chris@0 160 // https://www.drupal.org/node/2346329.
Chris@0 161 if ($storage_definition instanceof BaseFieldDefinition) {
Chris@0 162 $storage_definition
Chris@0 163 ->setName($name)
Chris@0 164 ->setTargetEntityTypeId($entity_type_id)
Chris@0 165 ->setProvider($provider)
Chris@0 166 ->setTargetBundle(NULL);
Chris@0 167 }
Chris@0 168 $this->entityManager->clearCachedDefinitions();
Chris@0 169 $this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
Chris@0 170 }
Chris@0 171
Chris@0 172 /**
Chris@0 173 * {@inheritdoc}
Chris@0 174 */
Chris@0 175 public function getFieldStorageDefinition($name, $entity_type_id) {
Chris@0 176 $storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
Chris@0 177 return isset($storage_definitions[$name]) ? clone $storage_definitions[$name] : NULL;
Chris@0 178 }
Chris@0 179
Chris@0 180 /**
Chris@0 181 * {@inheritdoc}
Chris@0 182 */
Chris@0 183 public function updateFieldStorageDefinition(FieldStorageDefinitionInterface $storage_definition) {
Chris@0 184 $original = $this->getFieldStorageDefinition($storage_definition->getName(), $storage_definition->getTargetEntityTypeId());
Chris@0 185 $this->entityManager->clearCachedDefinitions();
Chris@0 186 $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $original);
Chris@0 187 }
Chris@0 188
Chris@0 189 /**
Chris@0 190 * {@inheritdoc}
Chris@0 191 */
Chris@0 192 public function uninstallFieldStorageDefinition(FieldStorageDefinitionInterface $storage_definition) {
Chris@0 193 $this->entityManager->clearCachedDefinitions();
Chris@0 194 $this->entityManager->onFieldStorageDefinitionDelete($storage_definition);
Chris@0 195 }
Chris@0 196
Chris@0 197 /**
Chris@0 198 * Performs an entity type definition update.
Chris@0 199 *
Chris@0 200 * @param string $op
Chris@0 201 * The operation to perform, either static::DEFINITION_CREATED or
Chris@0 202 * static::DEFINITION_UPDATED.
Chris@0 203 * @param string $entity_type_id
Chris@0 204 * The entity type ID.
Chris@0 205 */
Chris@0 206 protected function doEntityUpdate($op, $entity_type_id) {
Chris@0 207 $entity_type = $this->entityManager->getDefinition($entity_type_id);
Chris@0 208 switch ($op) {
Chris@0 209 case static::DEFINITION_CREATED:
Chris@0 210 $this->entityManager->onEntityTypeCreate($entity_type);
Chris@0 211 break;
Chris@0 212
Chris@0 213 case static::DEFINITION_UPDATED:
Chris@0 214 $original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
Chris@0 215 $this->entityManager->onEntityTypeUpdate($entity_type, $original);
Chris@0 216 break;
Chris@0 217 }
Chris@0 218 }
Chris@0 219
Chris@0 220 /**
Chris@0 221 * Performs a field storage definition update.
Chris@0 222 *
Chris@0 223 * @param string $op
Chris@0 224 * The operation to perform, possible values are static::DEFINITION_CREATED,
Chris@0 225 * static::DEFINITION_UPDATED or static::DEFINITION_DELETED.
Chris@0 226 * @param array|null $storage_definition
Chris@0 227 * The new field storage definition.
Chris@0 228 * @param array|null $original_storage_definition
Chris@0 229 * The original field storage definition.
Chris@0 230 */
Chris@0 231 protected function doFieldUpdate($op, $storage_definition = NULL, $original_storage_definition = NULL) {
Chris@0 232 switch ($op) {
Chris@0 233 case static::DEFINITION_CREATED:
Chris@0 234 $this->entityManager->onFieldStorageDefinitionCreate($storage_definition);
Chris@0 235 break;
Chris@0 236
Chris@0 237 case static::DEFINITION_UPDATED:
Chris@0 238 $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $original_storage_definition);
Chris@0 239 break;
Chris@0 240
Chris@0 241 case static::DEFINITION_DELETED:
Chris@0 242 $this->entityManager->onFieldStorageDefinitionDelete($original_storage_definition);
Chris@0 243 break;
Chris@0 244 }
Chris@0 245 }
Chris@0 246
Chris@0 247 /**
Chris@0 248 * Gets a list of changes to entity type and field storage definitions.
Chris@0 249 *
Chris@0 250 * @return array
Chris@0 251 * An associative array keyed by entity type id of change descriptors. Every
Chris@0 252 * entry is an associative array with the following optional keys:
Chris@0 253 * - entity_type: a scalar having only the DEFINITION_UPDATED value.
Chris@0 254 * - field_storage_definitions: an associative array keyed by field name of
Chris@0 255 * scalars having one value among:
Chris@0 256 * - DEFINITION_CREATED
Chris@0 257 * - DEFINITION_UPDATED
Chris@0 258 * - DEFINITION_DELETED
Chris@0 259 */
Chris@0 260 protected function getChangeList() {
Chris@0 261 $this->entityManager->useCaches(FALSE);
Chris@0 262 $change_list = [];
Chris@0 263
Chris@0 264 foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) {
Chris@0 265 $original = $this->entityManager->getLastInstalledDefinition($entity_type_id);
Chris@0 266
Chris@0 267 // @todo Support non-storage-schema-changing definition updates too:
Chris@0 268 // https://www.drupal.org/node/2336895.
Chris@0 269 if (!$original) {
Chris@0 270 $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_CREATED;
Chris@0 271 }
Chris@0 272 else {
Chris@0 273 if ($this->requiresEntityStorageSchemaChanges($entity_type, $original)) {
Chris@0 274 $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED;
Chris@0 275 }
Chris@0 276
Chris@0 277 if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) {
Chris@0 278 $field_changes = [];
Chris@0 279 $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
Chris@0 280 $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id);
Chris@0 281
Chris@0 282 // Detect created field storage definitions.
Chris@0 283 foreach (array_diff_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
Chris@0 284 $field_changes[$field_name] = static::DEFINITION_CREATED;
Chris@0 285 }
Chris@0 286
Chris@0 287 // Detect deleted field storage definitions.
Chris@0 288 foreach (array_diff_key($original_storage_definitions, $storage_definitions) as $field_name => $original_storage_definition) {
Chris@0 289 $field_changes[$field_name] = static::DEFINITION_DELETED;
Chris@0 290 }
Chris@0 291
Chris@0 292 // Detect updated field storage definitions.
Chris@0 293 foreach (array_intersect_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) {
Chris@0 294 // @todo Support non-storage-schema-changing definition updates too:
Chris@0 295 // https://www.drupal.org/node/2336895. So long as we're checking
Chris@0 296 // based on schema change requirements rather than definition
Chris@0 297 // equality, skip the check if the entity type itself needs to be
Chris@0 298 // updated, since that can affect the schema of all fields, so we
Chris@0 299 // want to process that update first without reporting false
Chris@0 300 // positives here.
Chris@0 301 if (!isset($change_list[$entity_type_id]['entity_type']) && $this->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) {
Chris@0 302 $field_changes[$field_name] = static::DEFINITION_UPDATED;
Chris@0 303 }
Chris@0 304 }
Chris@0 305
Chris@0 306 if ($field_changes) {
Chris@0 307 $change_list[$entity_type_id]['field_storage_definitions'] = $field_changes;
Chris@0 308 }
Chris@0 309 }
Chris@0 310 }
Chris@0 311 }
Chris@0 312
Chris@0 313 // @todo Support deleting entity definitions when we support base field
Chris@0 314 // purging. See https://www.drupal.org/node/2282119.
Chris@0 315
Chris@0 316 $this->entityManager->useCaches(TRUE);
Chris@0 317
Chris@0 318 return array_filter($change_list);
Chris@0 319 }
Chris@0 320
Chris@0 321 /**
Chris@0 322 * Checks if the changes to the entity type requires storage schema changes.
Chris@0 323 *
Chris@0 324 * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type
Chris@0 325 * The updated entity type definition.
Chris@0 326 * @param \Drupal\Core\Entity\EntityTypeInterface $original
Chris@0 327 * The original entity type definition.
Chris@0 328 *
Chris@0 329 * @return bool
Chris@0 330 * TRUE if storage schema changes are required, FALSE otherwise.
Chris@0 331 */
Chris@0 332 protected function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) {
Chris@0 333 $storage = $this->entityManager->getStorage($entity_type->id());
Chris@0 334 return ($storage instanceof EntityStorageSchemaInterface) && $storage->requiresEntityStorageSchemaChanges($entity_type, $original);
Chris@0 335 }
Chris@0 336
Chris@0 337 /**
Chris@0 338 * Checks if the changes to the storage definition requires schema changes.
Chris@0 339 *
Chris@0 340 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition
Chris@0 341 * The updated field storage definition.
Chris@0 342 * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $original
Chris@0 343 * The original field storage definition.
Chris@0 344 *
Chris@0 345 * @return bool
Chris@0 346 * TRUE if storage schema changes are required, FALSE otherwise.
Chris@0 347 */
Chris@0 348 protected function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
Chris@0 349 $storage = $this->entityManager->getStorage($storage_definition->getTargetEntityTypeId());
Chris@0 350 return ($storage instanceof DynamicallyFieldableEntityStorageSchemaInterface) && $storage->requiresFieldStorageSchemaChanges($storage_definition, $original);
Chris@0 351 }
Chris@0 352
Chris@0 353 }