diff --git a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php index 3b1e7bd660..956c27e71b 100644 --- a/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php +++ b/core/modules/system/src/Tests/Entity/EntityDefinitionTestTrait.php @@ -111,12 +111,17 @@ protected function updateEntityTypeToRevisionableAndTranslatable() { * * @param string $type * (optional) The field type for the new field. Defaults to 'string'. + * @param string $entity_type_id + * (optional) The entity type ID the base field should be attached to. + * @param bool $is_revisionable + * (optional) If the base field should be revisionable or not. */ - protected function addBaseField($type = 'string') { + protected function addBaseField($type = 'string', $entity_type_id = 'entity_test_update', $is_revisionable = FALSE) { $definitions['new_base_field'] = BaseFieldDefinition::create($type) ->setName('new_base_field') + ->setRevisionable($is_revisionable) ->setLabel(t('A new base field')); - $this->state->set('entity_test_update.additional_base_field_definitions', $definitions); + $this->state->set($entity_type_id . '.additional_base_field_definitions', $definitions); } /** @@ -167,9 +172,12 @@ protected function makeBaseFieldEntityKey() { /** * Removes the new base field from the 'entity_test_update' entity type. + * + * @param string $entity_type_id + * (optional) The entity type ID the base field should be attached to. */ - protected function removeBaseField() { - $this->state->delete('entity_test_update.additional_base_field_definitions'); + protected function removeBaseField($entity_type_id = 'entity_test_update') { + $this->state->delete($entity_type_id . '.additional_base_field_definitions'); } /** diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php index cbe7ba4c9b..50ae3771a7 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMul.php @@ -2,6 +2,8 @@ namespace Drupal\entity_test\Entity; +use Drupal\Core\Entity\EntityTypeInterface; + /** * Defines the test entity class. * @@ -44,4 +46,11 @@ */ class EntityTestMul extends EntityTest { + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + return parent::baseFieldDefinitions($entity_type) + \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []); + } + } diff --git a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php index f01675f602..2bfdf41419 100644 --- a/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php +++ b/core/modules/system/tests/modules/entity_test/src/Entity/EntityTestMulRev.php @@ -2,6 +2,8 @@ namespace Drupal\entity_test\Entity; +use Drupal\Core\Entity\EntityTypeInterface; + /** * Defines the test entity class. * @@ -47,4 +49,11 @@ */ class EntityTestMulRev extends EntityTestRev { + /** + * {@inheritdoc} + */ + public static function baseFieldDefinitions(EntityTypeInterface $entity_type) { + return parent::baseFieldDefinitions($entity_type) + \Drupal::state()->get($entity_type->id() . '.additional_base_field_definitions', []); + } + } diff --git a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php index 5620450d65..d150fc1f76 100644 --- a/core/modules/views/tests/src/Unit/EntityViewsDataTest.php +++ b/core/modules/views/tests/src/Unit/EntityViewsDataTest.php @@ -18,6 +18,7 @@ use Drupal\Core\Field\Plugin\Field\FieldType\StringItem; use Drupal\Core\Field\Plugin\Field\FieldType\UriItem; use Drupal\Core\Field\Plugin\Field\FieldType\UuidItem; +use Drupal\Core\State\StateInterface; use Drupal\Core\TypedData\TypedDataManagerInterface; use Drupal\text\Plugin\Field\FieldType\TextLongItem; use Drupal\entity_test\Entity\EntityTest; @@ -25,6 +26,7 @@ use Drupal\entity_test\Entity\EntityTestMulRev; use Drupal\Tests\UnitTestCase; use Drupal\views\EntityViewsData; +use Prophecy\Argument; use Symfony\Component\DependencyInjection\ContainerBuilder; /** @@ -124,10 +126,14 @@ protected function setUp() { ->method('getDefaultFieldSettings') ->willReturn([]); + $state = $this->prophesize(StateInterface::class); + $state->get(Argument::any(), [])->willReturn([]); + $container = new ContainerBuilder(); $container->set('plugin.manager.field.field_type', $field_type_manager); $container->set('entity.manager', $this->entityManager); $container->set('typed_data_manager', $typed_data_manager); + $container->set('state', $state->reveal()); \Drupal::setContainer($container); } diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php index 11b79b3d92..e50007d9e4 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -389,10 +389,12 @@ public function testBundleFieldCreateDeleteWithExistingEntities() { /** * Tests deleting a base field when it has existing data. + * + * @dataProvider baseFieldDeleteWithExistingDataTestCases */ - public function testBaseFieldDeleteWithExistingData() { + public function testBaseFieldDeleteWithExistingData($entity_type_id, $create_entity_revision, $base_field_revisionable) { /** @var \Drupal\Core\Entity\Sql\SqlEntityStorageInterface $storage */ - $storage = $this->entityManager->getStorage('entity_test_update'); + $storage = $this->entityManager->getStorage($entity_type_id); $schema_handler = $this->database->schema(); // Create an entity without the base field, to ensure NULL values are not @@ -401,23 +403,29 @@ public function testBaseFieldDeleteWithExistingData() { $entity->save(); // Add the base field and run the update. - $this->addBaseField(); + $this->addBaseField('string', $entity_type_id, $base_field_revisionable); $this->entityDefinitionUpdateManager->applyUpdates(); /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ $table_mapping = $storage->getTableMapping(); - $storage_definition = $this->entityManager->getLastInstalledFieldStorageDefinitions('entity_test_update')['new_base_field']; + $storage_definition = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type_id)['new_base_field']; // Save an entity with the base field populated. $entity = $storage->create(['new_base_field' => 'foo']); $entity->save(); + if ($create_entity_revision) { + $entity->setNewRevision(TRUE); + $entity->new_base_field = 'bar'; + $entity->save(); + } + // Remove the base field and apply updates. - $this->removeBaseField(); + $this->removeBaseField($entity_type_id); $this->entityDefinitionUpdateManager->applyUpdates(); // Check that the base field's column is deleted. - $this->assertFalse($schema_handler->fieldExists('entity_test_update', 'new_base_field'), 'Column deleted from shared table for new_base_field.'); + $this->assertFalse($schema_handler->fieldExists($entity_type_id, 'new_base_field'), 'Column deleted from shared table for new_base_field.'); // Check that a dedicated 'deleted' table was created for the deleted base // field. @@ -436,13 +444,49 @@ public function testBaseFieldDeleteWithExistingData() { 'bundle' => $entity->bundle(), 'deleted' => '1', 'entity_id' => $entity->id(), - 'revision_id' => $entity->id(), + 'revision_id' => $create_entity_revision ? $entity->getRevisionId() : $entity->id(), 'langcode' => $entity->language()->getId(), 'delta' => '0', 'new_base_field_value' => $entity->new_base_field->value, ]; $this->assertSame($expected, (array) $result[0]); + if ($create_entity_revision) { + $dedicated_deleted_revision_table_name = $table_mapping->getDedicatedRevisionTableName($storage_definition, TRUE); + $this->assertTrue($schema_handler->tableExists($dedicated_deleted_revision_table_name), 'A dedicated revision table was created for the deleted new_base_field.'); + + $result = $this->database->select($dedicated_deleted_revision_table_name, 't') + ->fields('t') + ->orderBy('revision_id', 'DESC') + ->execute() + ->fetchAll(); + // Only one row will be created for non-revisionable base fields. + $this->assertCount($base_field_revisionable ? 2 : 1, $result); + + $this->assertSame([ + 'bundle' => $entity->bundle(), + 'deleted' => '1', + 'entity_id' => $entity->id(), + 'revision_id' => $entity->getRevisionId(), + 'langcode' => $entity->language()->getId(), + 'delta' => '0', + 'new_base_field_value' => 'bar', + ], (array) $result[0]); + + // Two rows only exist if the base field is revisionable. + if ($base_field_revisionable) { + $this->assertSame([ + 'bundle' => $entity->bundle(), + 'deleted' => '1', + 'entity_id' => $entity->id(), + 'revision_id' => '2', + 'langcode' => $entity->language()->getId(), + 'delta' => '0', + 'new_base_field_value' => 'foo', + ], (array) $result[1]); + } + } + // Check that the field storage definition is marked for purging. $deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions(); $this->assertArrayHasKey($storage_definition->getUniqueStorageIdentifier(), $deleted_storage_definitions, 'The base field is marked for purging.'); @@ -453,6 +497,48 @@ public function testBaseFieldDeleteWithExistingData() { $deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions(); $this->assertEmpty($deleted_storage_definitions, 'The base field has been deleted.'); $this->assertFalse($schema_handler->tableExists($dedicated_deleted_table_name), 'A dedicated table was created for the deleted new_base_field.'); + + if (isset($dedicated_deleted_revision_table_name)) { + $this->assertFalse($schema_handler->tableExists($dedicated_deleted_revision_table_name), 'A dedicated table was created for the deleted new_base_field.'); + } + } + + /** + * Test cases for ::testBaseFieldDeleteWithExistingData. + */ + public function baseFieldDeleteWithExistingDataTestCases() { + return [ + 'Non-revisionable entity type' => [ + 'entity_test_update', + FALSE, + FALSE, + ], + 'Non-revisionable custom data table' => [ + 'entity_test_mul', + FALSE, + FALSE, + ], + 'Non-revisionable entity type, revisionable base field' => [ + 'entity_test_update', + FALSE, + TRUE, + ], + 'Non-revisionable custom data table, revisionable base field' => [ + 'entity_test_mul', + FALSE, + TRUE, + ], + 'Revisionable entity type, non revisionable base field' => [ + 'entity_test_mulrev', + TRUE, + FALSE, + ], + 'Revisionable entity type, revisionable base field' => [ + 'entity_test_mulrev', + TRUE, + TRUE, + ], + ]; } /**