diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php index 8f75bdeebfe516e0bd8a0d80789783d80ca022e5..1db9e97564fafd5ae7f52661f2d52efb23b97b17 100644 --- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php +++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php @@ -18,27 +18,6 @@ class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInterface { use StringTranslationTrait; - /** - * Indicates that a definition has just been created. - * - * @var int - */ - const DEFINITION_CREATED = 1; - - /** - * Indicates that a definition has changes. - * - * @var int - */ - const DEFINITION_UPDATED = 2; - - /** - * Indicates that a definition has just been deleted. - * - * @var int - */ - const DEFINITION_DELETED = 3; - /** * The entity manager service. * @@ -70,6 +49,22 @@ public function getChangeSummary() { $summary = array(); foreach ($this->getChangeList() as $entity_type_id => $change_list) { + // Process entity type definition changes. + if (!empty($change_list['entity_type'])) { + $entity_type = $this->entityManager->getDefinition($entity_type_id); + $t_args = array('%entity_type' => $entity_type->getLabel()); + + switch ($change_list['entity_type']) { + case static::DEFINITION_CREATED: + $summary[$entity_type_id][] = $this->t('Create the %entity_type entity type.', $t_args); + break; + + case static::DEFINITION_UPDATED: + $summary[$entity_type_id][] = $this->t('Update the %entity_type entity type.', $t_args); + break; + } + } + // Process field storage definition changes. if (!empty($change_list['field_storage_definitions'])) { $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); @@ -91,11 +86,6 @@ public function getChangeSummary() { } } } - // Process entity type definition changes. - if (!empty($change_list['entity_type']) && $change_list['entity_type'] == static::DEFINITION_UPDATED) { - $entity_type = $this->entityManager->getDefinition($entity_type_id); - $summary[$entity_type_id][] = $this->t('Update the %entity_type entity type.', array('%entity_type' => $entity_type->getLabel())); - } } return $summary; @@ -110,10 +100,19 @@ public function applyUpdates() { // this is necessary when you change an entity type from non-revisionable // to revisionable and at the same time add revisionable fields to the // entity type. - if (!empty($change_list['entity_type']) && $change_list['entity_type'] == static::DEFINITION_UPDATED) { + if (!empty($change_list['entity_type'])) { $entity_type = $this->entityManager->getDefinition($entity_type_id); - $original = $this->entityManager->getLastInstalledDefinition($entity_type_id); - $this->entityManager->onEntityTypeUpdate($entity_type, $original); + + switch ($change_list['entity_type']) { + case static::DEFINITION_CREATED: + $this->entityManager->onEntityTypeCreate($entity_type); + break; + + case static::DEFINITION_UPDATED: + $original = $this->entityManager->getLastInstalledDefinition($entity_type_id); + $this->entityManager->onEntityTypeUpdate($entity_type, $original); + break; + } } // Process field storage definition changes. @@ -160,54 +159,55 @@ protected function getChangeList() { foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { $original = $this->entityManager->getLastInstalledDefinition($entity_type_id); - // Only manage changes to already installed entity types. Entity type - // installation is handled elsewhere (e.g., - // \Drupal\Core\Extension\ModuleHandler::install()). - if (!$original) { - continue; - } - // @todo Support non-storage-schema-changing definition updates too: // https://www.drupal.org/node/2336895. - if ($this->requiresEntityStorageSchemaChanges($entity_type, $original)) { - $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED; + if (!$original) { + $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_CREATED; } + else { + if ($this->requiresEntityStorageSchemaChanges($entity_type, $original)) { + $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED; + } - if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) { - $field_changes = array(); - $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); - $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id); + if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) { + $field_changes = array(); + $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); + $original_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id); - // Detect created field storage definitions. - foreach (array_diff_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) { - $field_changes[$field_name] = static::DEFINITION_CREATED; - } + // Detect created field storage definitions. + foreach (array_diff_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) { + $field_changes[$field_name] = static::DEFINITION_CREATED; + } - // Detect deleted field storage definitions. - foreach (array_diff_key($original_storage_definitions, $storage_definitions) as $field_name => $original_storage_definition) { - $field_changes[$field_name] = static::DEFINITION_DELETED; - } + // Detect deleted field storage definitions. + foreach (array_diff_key($original_storage_definitions, $storage_definitions) as $field_name => $original_storage_definition) { + $field_changes[$field_name] = static::DEFINITION_DELETED; + } - // Detect updated field storage definitions. - foreach (array_intersect_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) { - // @todo Support non-storage-schema-changing definition updates too: - // https://www.drupal.org/node/2336895. So long as we're checking - // based on schema change requirements rather than definition - // equality, skip the check if the entity type itself needs to be - // updated, since that can affect the schema of all fields, so we - // want to process that update first without reporting false - // positives here. - if (!isset($change_list[$entity_type_id]['entity_type']) && $this->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) { - $field_changes[$field_name] = static::DEFINITION_UPDATED; + // Detect updated field storage definitions. + foreach (array_intersect_key($storage_definitions, $original_storage_definitions) as $field_name => $storage_definition) { + // @todo Support non-storage-schema-changing definition updates too: + // https://www.drupal.org/node/2336895. So long as we're checking + // based on schema change requirements rather than definition + // equality, skip the check if the entity type itself needs to be + // updated, since that can affect the schema of all fields, so we + // want to process that update first without reporting false + // positives here. + if (!isset($change_list[$entity_type_id]['entity_type']) && $this->requiresFieldStorageSchemaChanges($storage_definition, $original_storage_definitions[$field_name])) { + $field_changes[$field_name] = static::DEFINITION_UPDATED; + } } - } - if ($field_changes) { - $change_list[$entity_type_id]['field_storage_definitions'] = $field_changes; + if ($field_changes) { + $change_list[$entity_type_id]['field_storage_definitions'] = $field_changes; + } } } } + // @todo Support deleting entity definitions when we support base field + // purging. See https://www.drupal.org/node/2282119. + return array_filter($change_list); } diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php index c6ead5b9acc8979993dfb40e80c45d13f5e6c9b5..25bbdade1e8e7750cde31cd92a4a7d7151267011 100644 --- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php @@ -34,6 +34,27 @@ */ interface EntityDefinitionUpdateManagerInterface { + /** + * Indicates that a definition has just been created. + * + * @var int + */ + const DEFINITION_CREATED = 1; + + /** + * Indicates that a definition has changes. + * + * @var int + */ + const DEFINITION_UPDATED = 2; + + /** + * Indicates that a definition has just been deleted. + * + * @var int + */ + const DEFINITION_DELETED = 3; + /** * Checks if there are any definition updates that need to be applied. * diff --git a/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php b/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php index 73a9f25c1d644783d091ca6e29ae6af5e2e5e0d3..f94db02971030bebb53ec4f020470e6b1d6e8f95 100644 --- a/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php +++ b/core/modules/locale/src/Tests/LocaleTranslatedSchemaDefinitionTest.php @@ -31,6 +31,8 @@ protected function setUp() { parent::setUp(); ConfigurableLanguage::createFromLangcode('fr')->save(); $this->config('system.site')->set('langcode', 'fr')->save(); + // Make sure new entity type definitions are processed. + \Drupal::service('entity.definition_update_manager')->applyUpdates(); // Clear all caches so that the base field definition, its cache in the // entity manager, the t() cache, etc. are all cleared. drupal_flush_all_caches(); diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php index ffc5a2e8180551314f8d3a9379942dcc4cd94565..353dd4d8ca39394d11ad73b6c6567762cf15aa77 100644 --- a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php +++ b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php @@ -15,6 +15,15 @@ */ trait EntityDefinitionTestTrait { + /** + * Enables a new entity type definition. + */ + protected function enableNewEntityType() { + $this->state->set('entity_test_new', TRUE); + $this->entityManager->clearCachedDefinitions(); + $this->entityDefinitionUpdateManager->applyUpdates(); + } + /** * Resets the entity type definition. */ diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php b/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php index 33c1a05c821d1b588e461e78e779048b25380403..05d2a06f88f937c993c76d8dd44dee8826a5f688 100644 --- a/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php +++ b/core/modules/system/src/Tests/Entity/EntityDefinitionUpdateTest.php @@ -54,6 +54,26 @@ protected function setUp() { } } + /** + * Tests that new entity type definitions are correctly handled. + */ + public function testNewEntityType() { + $entity_type_id = 'entity_test_new'; + $schema = $this->database->schema(); + + // Check that the "entity_test_new" is not defined. + $entity_types = $this->entityManager->getDefinitions(); + $this->assertFalse(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type does not exist.'); + $this->assertFalse($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type does not exist.'); + + // Check that the "entity_test_new" is now defined and the related schema + // has been created. + $this->enableNewEntityType(); + $entity_types = $this->entityManager->getDefinitions(); + $this->assertTrue(isset($entity_types[$entity_type_id]), 'The "entity_test_new" entity type exists.'); + $this->assertTrue($schema->tableExists($entity_type_id), 'Schema for the "entity_test_new" entity type has been created.'); + } + /** * Tests when no definition update is needed. */ diff --git a/core/modules/system/src/Tests/Update/UpdateScriptTest.php b/core/modules/system/src/Tests/Update/UpdateScriptTest.php index 5b09674e356a873a3bbee3200dbf23039a196bb7..2da975fb5c3ee19a015aa223526ea8edd17abac9 100644 --- a/core/modules/system/src/Tests/Update/UpdateScriptTest.php +++ b/core/modules/system/src/Tests/Update/UpdateScriptTest.php @@ -32,6 +32,8 @@ protected function setUp() { parent::setUp(); $this->update_url = $GLOBALS['base_url'] . '/update.php'; $this->update_user = $this->drupalCreateUser(array('administer software updates', 'access site in maintenance mode')); + // Make sure updates for new entity type definitions are processed. + \Drupal::service('entity.definition_update_manager')->applyUpdates(); } /** diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module index 9317d3c7d394e789cc750cf280da8b979303d76f..64418c7291e55b2f9692f954d1f69ab6262cf4aa 100644 --- a/core/modules/system/tests/modules/entity_test/entity_test.module +++ b/core/modules/system/tests/modules/entity_test/entity_test.module @@ -74,10 +74,12 @@ function entity_test_entity_types($filter = NULL) { * Implements hook_entity_type_alter(). */ function entity_test_entity_type_alter(array &$entity_types) { + $state = \Drupal::state(); + /** @var $entity_types \Drupal\Core\Entity\EntityTypeInterface[] */ foreach (entity_test_entity_types() as $entity_type) { // Optionally specify a translation handler for testing translations. - if (\Drupal::state()->get('entity_test.translation')) { + if ($state->get('entity_test.translation')) { $translation = $entity_types[$entity_type]->get('translation'); $translation[$entity_type] = TRUE; $entity_types[$entity_type]->set('translation', $translation); @@ -85,7 +87,12 @@ function entity_test_entity_type_alter(array &$entity_types) { } // Allow entity_test_update tests to override the entity type definition. - $entity_types['entity_test_update'] = \Drupal::state()->get('entity_test_update.entity_type', $entity_types['entity_test_update']); + $entity_types['entity_test_update'] = $state->get('entity_test_update.entity_type', $entity_types['entity_test_update']); + + // Enable the entity_test_new only when needed. + if (!$state->get('entity_test_new')) { + unset($entity_types['entity_test_new']); + } } /** diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNew.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNew.php new file mode 100644 index 0000000000000000000000000000000000000000..9adb52d046434eb1b275d03642e3315e3d51eb40 --- /dev/null +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestNew.php @@ -0,0 +1,30 @@ +<?php + +/** + * @file + * Contains \Drupal\entity_test\Entity\EntityTestNew. + */ + +namespace Drupal\entity_test\Entity; + +/** + * Defines the test entity class for testing definition addition. + * + * This entity type is initially not defined. It is enabled when needed to test + * the related updates. + * + * @ContentEntityType( + * id = "entity_test_new", + * label = @Translation("New test entity"), + * base_table = "entity_test_new", + * entity_keys = { + * "id" = "id", + * "uuid" = "uuid", + * "bundle" = "type", + * "label" = "name", + * "langcode" = "langcode", + * } + * ) + */ +class EntityTestNew extends EntityTest { +}