diff --git a/core/lib/Drupal/Core/Entity/EntityTypeListener.php b/core/lib/Drupal/Core/Entity/EntityTypeListener.php index 8e256f41b2..a8c90c4861 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeListener.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeListener.php @@ -128,7 +128,7 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En $storage->onFieldableEntityTypeUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } - if ($sandbox === NULL || (isset($sandbox['#finished']) && $sandbox['#finished'] === 1)) { + if ($sandbox === NULL || (isset($sandbox['#finished']) && $sandbox['#finished'] == 1)) { $this->eventDispatcher->dispatch(EntityTypeEvents::UPDATE, new EntityTypeEvent($entity_type, $original)); $this->entityLastInstalledSchemaRepository->setLastInstalledDefinition($entity_type); diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index 6387d70427..dba62bdc74 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -450,10 +450,7 @@ protected function preUpdateEntityTypeSchema(EntityTypeInterface $entity_type, E // Filter out tables which are not part of the table mapping. $schema = array_intersect_key($schema, $temporary_table_names); foreach ($schema as $table_name => $table_schema) { - $temp_table_name = $temporary_table_names[$table_name]; - if (!$schema_handler->tableExists($temp_table_name)) { - $schema_handler->createTable($temp_table_name, $table_schema); - } + $schema_handler->createTable($temporary_table_names[$table_name], $table_schema); } // Create dedicated field tables. @@ -464,10 +461,7 @@ protected function preUpdateEntityTypeSchema(EntityTypeInterface $entity_type, E // Filter out tables which are not part of the table mapping. $schema = array_intersect_key($schema, $temporary_table_names); foreach ($schema as $table_name => $table_schema) { - $temp_table_name = $temporary_table_names[$table_name]; - if (!$schema_handler->tableExists($temp_table_name)) { - $schema_handler->createTable($temp_table_name, $table_schema); - } + $schema_handler->createTable($temporary_table_names[$table_name], $table_schema); } } } @@ -523,8 +517,8 @@ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type, $this->createDedicatedTableSchema($field_storage_definition, TRUE); } elseif ($new_table_mapping->allowsSharedTableStorage($field_storage_definition)) { - // The shared tables are already fully created, but we need to save the - // per-field schema definitions for later use. + // The shared tables are already fully created, but we need to save + // the per-field schema definitions for later use. $this->createSharedTableSchema($field_storage_definition, TRUE); } } @@ -547,6 +541,8 @@ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type, // At this point the update process either finished successfully or any // error has been thrown already, so we can drop the backup entity tables. + // @todo Decide whether we should keep these tables around. + // @see https://www.drupal.org/project/drupal/issues/3024728 foreach ($backup_table_names as $original_table_name => $backup_table_name) { $this->database->schema()->dropTable($backup_table_name); } diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php b/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php index 0dd808e9c2..bec6f5602f 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php @@ -24,8 +24,10 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En $has_data = $original_storage->hasData(); // We cannot support updating the schema of an entity type from revisionable - // to non-revisionable or translatable to non-translatable because that - // implies data loss. + // to non-revisionable or translatable to non-translatable because that can + // lead to unintended data loss. + // @todo Add support for these conversions in case there is no data loss. + // @see https://www.drupal.org/project/drupal/issues/3024727 $convert_rev_to_non_rev = $original->isRevisionable() && !$entity_type->isRevisionable(); $convert_mul_to_non_mul = $original->isTranslatable() && !$entity_type->isTranslatable(); if ($has_data && ($convert_rev_to_non_rev || $convert_mul_to_non_mul)) { @@ -75,7 +77,7 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En // If the data copying has finished successfully, allow the storage schema // 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) { + if ($sandbox['#finished'] == 1) { $this->postUpdateEntityTypeSchema($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } } diff --git a/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php b/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php index 2633beb929..8cf74829ea 100644 --- a/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php +++ b/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php @@ -302,7 +302,7 @@ protected function deleteEntityType() { * * @param bool $revisionable * (optional) Whether the entity type should be revisionable or not. - * Defaults toFALSE. + * Defaults to FALSE. * @param bool $translatable * (optional) Whether the entity type should be translatable or not. * Defaults to FALSE. @@ -353,7 +353,7 @@ protected function getUpdatedEntityTypeDefinition($revisionable = FALSE, $transl * * @param bool $revisionable * (optional) Whether the entity type should be revisionable or not. - * Defaults toFALSE. + * Defaults to FALSE. * @param bool $translatable * (optional) Whether the entity type should be translatable or not. * Defaults to FALSE. diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php index 3c764d6095..e2f041804a 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php @@ -4,6 +4,7 @@ use Drupal\Core\Entity\EntityStorageException; use Drupal\Core\Field\BaseFieldDefinition; +use Drupal\Core\Site\Settings; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Tests\system\Functional\Entity\Traits\EntityDefinitionTestTrait; @@ -86,6 +87,11 @@ protected function setUp() { // Enable an additional language. ConfigurableLanguage::createFromLangcode('ro')->save(); + + // Force the update function to convert one entity at a time. + $settings = Settings::getAll(); + $settings['entity_update_batch_size'] = 1; + new Settings($settings); } /** @@ -115,8 +121,12 @@ public function testFieldableEntityTypeUpdates($initial_rev, $initial_mul, $new_ $this->setExpectedException(EntityStorageException::class, 'Converting an entity type from revisionable to non-revisionable or from translatable to non-translatable is not supported.'); } + // Simulate a batch run since we are converting the entities one by one. $sandbox = []; - $this->entityDefinitionUpdateManager->updateFieldableEntityType($updated_entity_type, $updated_field_storage_definitions, $sandbox); + do { + $this->entityDefinitionUpdateManager->updateFieldableEntityType($updated_entity_type, $updated_field_storage_definitions, $sandbox); + } while ($sandbox['#finished'] != 1); + $this->assertEntityTypeSchema($new_rev, $new_mul); $this->assertEntityData($initial_rev, $initial_mul); }