diff --git a/core/lib/Drupal/Core/Entity/EntityTypeListener.php b/core/lib/Drupal/Core/Entity/EntityTypeListener.php index 6706fef..c8b23f6 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeListener.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeListener.php @@ -128,11 +128,13 @@ public function onEntityTypeSchemaUpdate(EntityTypeInterface $entity_type, Entit $storage->onEntityTypeSchemaUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } - $this->eventDispatcher->dispatch(EntityTypeEvents::UPDATE, new EntityTypeEvent($entity_type, $original)); + if ($sandbox === NULL || (isset($sandbox['#finished']) && $sandbox['#finished'] === 1)) { + $this->eventDispatcher->dispatch(EntityTypeEvents::UPDATE, new EntityTypeEvent($entity_type, $original)); - $this->entityLastInstalledSchemaRepository->setLastInstalledDefinition($entity_type); - if ($entity_type->entityClassImplements(FieldableEntityInterface::class)) { - $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinitions($entity_type_id, $field_storage_definitions); + $this->entityLastInstalledSchemaRepository->setLastInstalledDefinition($entity_type); + if ($entity_type->entityClassImplements(FieldableEntityInterface::class)) { + $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinitions($entity_type_id, $field_storage_definitions); + } } } diff --git a/core/lib/Drupal/Core/Entity/EntityTypeSchemaListenerTrait.php b/core/lib/Drupal/Core/Entity/EntityTypeSchemaListenerTrait.php index bd87146..d22ca35 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeSchemaListenerTrait.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeSchemaListenerTrait.php @@ -25,14 +25,20 @@ public function onEntityTypeSchemaUpdate(EntityTypeInterface $entity_type, Entit $this->preUpdateEntityTypeSchema($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } - // Copy data. + // Copy data from the original storage to the temporary one. if ($sandbox['original_storage']->hasData()) { $this->copyData($entity_type, $original, $sandbox); } + else { + // If there is no existing data, we still need to run the + // post-schema-update tasks. + $sandbox['#finished'] = 1; + } // If the data copying has finished successfully, allow the storage schema - // to do any required cleanup tasks. - if ($sandbox['#finished'] == 1) { + // to do any required cleanup tasks. For example, this process should take + // care of transforming the temporary storage into the current storage. + if ($sandbox['#finished'] === 1) { $this->postUpdateEntityTypeSchema($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } } @@ -88,41 +94,70 @@ protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterfac $id_key = $entity_type->getKey('id'); $revision_id_key = $entity_type->getKey('revision'); $revision_default_key = $entity_type->getRevisionMetadataKey('revision_default'); + $langcode_key = $entity_type->getKey('langcode'); + $default_langcode_key = $entity_type->getKey('default_langcode'); $revision_translation_affected_key = $entity_type->getKey('revision_translation_affected'); $temporary_storage = $sandbox['temporary_storage']; $original_storage = $sandbox['original_storage']; + // If both the original entity type and the updated one are revisionable, we + // need to handle all their revisions. + $load_revisions = $original->isRevisionable() && $entity_type->isRevisionable(); + // If 'progress' is not set, then this will be the first run of the batch. if (!isset($sandbox['progress'])) { $sandbox['progress'] = 0; $sandbox['current_id'] = 0; - $sandbox['max'] = (int) $original_storage->getQuery()->count()->execute(); + if ($load_revisions) { + $sandbox['max'] = (int) $original_storage->getQuery()->allRevisions()->count()->execute(); + } + else { + $sandbox['max'] = (int) $original_storage->getQuery()->count()->execute(); + } } - // Get the next entity IDs to migrate. + // Get the next entity identifiers to migrate. $step_size = Settings::get('entity_update_batch_size', 50); - $entity_ids = $original_storage->getQuery() - ->condition($id_key, $sandbox['current_id'], '>') - ->sort($id_key, 'ASC') + $query = $original_storage->getQuery(); + if ($load_revisions) { + $query->allRevisions(); + $identifier_field = $revision_id_key; + } + else { + $identifier_field = $id_key; + } + $entity_identifiers = $query + ->condition($identifier_field, $sandbox['current_id'], '>') + ->sort($identifier_field, 'ASC') ->range(0, $step_size) ->execute(); - $entities = $original_storage->loadMultiple($entity_ids); + $entities = $load_revisions ? $original_storage->loadMultipleRevisions(array_keys($entity_identifiers)) : $original_storage->loadMultiple($entity_identifiers); foreach ($entities as $entity_id => $entity) { try { - // @todo Only set the fields needed based on the type of the conversion. - // Set the revision ID to be same as the entity ID. - $entity->set($revision_id_key, $entity_id); + if (!$original->isRevisionable() && $entity_type->isRevisionable()) { + // Set the revision ID to be same as the entity ID. + $entity->set($revision_id_key, $entity_id); - // We had no revisions so far, so the existing data belongs to the - // default revision now. - $entity->set($revision_default_key, TRUE); + // We had no revisions so far, so the existing data belongs to the + // default revision now. + $entity->set($revision_default_key, TRUE); + } + + // Set the 'langcode' and 'default_langcode' values as needed. + if (!$original->isTranslatable() && $entity_type->isTranslatable()) { + $original_has_langcode = $entity->hasField($langcode_key) && !$entity->get($langcode_key)->isEmpty(); + $langcode = $original_has_langcode ? $entity->get($langcode_key) : \Drupal::languageManager()->getDefaultLanguage()->getId(); + + $entity->set($langcode_key, $langcode); + $entity->set($default_langcode_key, $langcode); + } // Set the 'revision_translation_affected' flag to TRUE to match the // previous API return value: if the field was not defined the value // returned was always TRUE. - if ($entity_type->isTranslatable()) { + if (!$original->isRevisionable() && !$original->isTranslatable() && $entity_type->isRevisionable() && $entity_type->isTranslatable()) { $entity->set($revision_translation_affected_key, TRUE); } @@ -146,7 +181,12 @@ protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterfac // If we're not in maintenance mode, the number of entities could change at // any time so make sure that we always use the latest record count. - $sandbox['max'] = (int) $original_storage->getQuery()->count()->execute(); + if ($load_revisions) { + $sandbox['max'] = (int) $original_storage->getQuery()->allRevisions()->count()->execute(); + } + else { + $sandbox['max'] = (int) $original_storage->getQuery()->count()->execute(); + } $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); }