diff --git a/core/core.services.yml b/core/core.services.yml index 66a46c6603..4490d3263b 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -599,7 +599,7 @@ services: arguments: ['@entity_type.manager', '@event_dispatcher', '@entity.last_installed_schema.repository', '@entity_field.manager', '@entity_field.deleted_fields_repository'] field_definition.listener: class: Drupal\Core\Field\FieldDefinitionListener - arguments: ['@entity_type.manager', '@entity_field.manager', '@keyvalue', '@cache.discovery'] + arguments: ['@entity_type.manager', '@entity_field.manager', '@keyvalue', '@cache.discovery', '@entity_field.deleted_fields_repository'] entity.form_builder: class: Drupal\Core\Entity\EntityFormBuilder arguments: ['@entity_type.manager', '@form_builder'] diff --git a/core/lib/Drupal/Core/Field/FieldDefinition.php b/core/lib/Drupal/Core/Field/FieldDefinition.php index ebefa6121b..ff59b487ca 100644 --- a/core/lib/Drupal/Core/Field/FieldDefinition.php +++ b/core/lib/Drupal/Core/Field/FieldDefinition.php @@ -44,6 +44,11 @@ class FieldDefinition extends ListDataDefinition implements FieldDefinitionInter */ protected $fieldStorageDefinition; + /** + * @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface + */ + protected $deletedFieldsRepository; + /** * Creates a new field definition. * @@ -62,21 +67,21 @@ public static function createFromFieldStorageDefinition(FieldStorageDefinitionIn * {@inheritdoc} */ public function getName() { - return $this->getFieldStorageDefinition()->getName(); + return $this->fieldStorageDefinition->getName(); } /** * {@inheritdoc} */ public function getType() { - return $this->getFieldStorageDefinition()->getType(); + return $this->fieldStorageDefinition->getType(); } /** * {@inheritdoc} */ public function getTargetEntityTypeId() { - return $this->getFieldStorageDefinition()->getTargetEntityTypeId(); + return $this->fieldStorageDefinition->getTargetEntityTypeId(); } /** @@ -263,9 +268,29 @@ public function setFieldStorageDefinition(FieldStorageDefinitionInterface $stora * {@inheritdoc} */ public function getFieldStorageDefinition() { + $deleted_storage_definitions = $this->getDeletedFieldsRepository()->getFieldStorageDefinitions(); + // If the storage definition associated with this field definition has been + // deleted, ensure we return the deleted version, so when deleting and + // purging a field definition, items are purged from the deleted storage. + if (isset($deleted_storage_definitions[$this->fieldStorageDefinition->getUniqueStorageIdentifier()])) { + return $deleted_storage_definitions[$this->fieldStorageDefinition->getUniqueStorageIdentifier()]; + } return $this->fieldStorageDefinition; } + /** + * Get the deleted fields repository service. + * + * @return \Drupal\Core\Field\DeletedFieldsRepositoryInterface + * The deleted fields repository service. + */ + protected function getDeletedFieldsRepository() { + if (!isset($this->deletedFieldsRepository)) { + $this->deletedFieldsRepository = \Drupal::service('entity_field.deleted_fields_repository'); + } + return $this->deletedFieldsRepository; + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Field/FieldDefinitionListener.php b/core/lib/Drupal/Core/Field/FieldDefinitionListener.php index 7b181f0dd7..f2c062b2d2 100644 --- a/core/lib/Drupal/Core/Field/FieldDefinitionListener.php +++ b/core/lib/Drupal/Core/Field/FieldDefinitionListener.php @@ -40,6 +40,13 @@ class FieldDefinitionListener implements FieldDefinitionListenerInterface { */ protected $entityFieldManager; + /** + * The deleted fields repository. + * + * @var \Drupal\Core\Field\DeletedFieldsRepositoryInterface + */ + protected $deletedFieldsRepository; + /** * Constructs a new FieldDefinitionListener. * @@ -51,12 +58,15 @@ class FieldDefinitionListener implements FieldDefinitionListenerInterface { * The key-value factory. * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend * The cache backend. + * @param \Drupal\Core\Field\DeletedFieldsRepositoryInterface $deleted_fields_repository + * The deleted fields repository. */ - public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, KeyValueFactoryInterface $key_value_factory, CacheBackendInterface $cache_backend) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityFieldManagerInterface $entity_field_manager, KeyValueFactoryInterface $key_value_factory, CacheBackendInterface $cache_backend, DeletedFieldsRepositoryInterface $deleted_fields_repository) { $this->entityTypeManager = $entity_type_manager; $this->entityFieldManager = $entity_field_manager; $this->keyValueFactory = $key_value_factory; $this->cacheBackend = $cache_backend; + $this->deletedFieldsRepository = $deleted_fields_repository; } /** @@ -118,6 +128,11 @@ public function onFieldDefinitionDelete(FieldDefinitionInterface $field_definiti $bundle = $field_definition->getTargetBundle(); $field_name = $field_definition->getName(); + $deleted_field_definitions = $this->deletedFieldsRepository->getFieldDefinitions(); + if (!isset($deleted_field_definitions[$field_definition->getUniqueIdentifier()])) { + $this->deletedFieldsRepository->addFieldDefinition($field_definition); + } + // Notify the storage about the field deletion. $this->entityTypeManager->getStorage($entity_type_id)->onFieldDefinitionDelete($field_definition); 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 6edfb32bee..1e0733e63a 100644 --- a/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php +++ b/core/modules/system/tests/src/Functional/Entity/Traits/EntityDefinitionTestTrait.php @@ -209,9 +209,11 @@ protected function addBundleField($type = 'string') { ->setLabel(t('A new bundle field')) ->setTargetEntityTypeId('entity_test_update'); $field_definitions['new_bundle_field'] = FieldDefinition::createFromFieldStorageDefinition($storage_definitions['new_bundle_field']) - ->setTargetBundle('test_bundle'); + ->setTargetBundle('entity_test_update'); $this->state->set('entity_test_update.additional_field_storage_definitions', $storage_definitions); - $this->state->set('entity_test_update.additional_bundle_field_definitions.test_bundle', $field_definitions); + $this->state->set('entity_test_update.additional_bundle_field_definitions.entity_test_update', $field_definitions); + + $this->entityManager->onFieldDefinitionCreate($field_definitions['new_bundle_field']); } /** @@ -225,8 +227,11 @@ protected function modifyBundleField() { * Removes the new bundle field from the 'entity_test_update' entity type. */ protected function removeBundleField() { + $field_definition = $this->entityManager->getFieldDefinitions('entity_test_update', 'entity_test_update')['new_bundle_field']; + $this->entityManager->onFieldDefinitionDelete($field_definition); + $this->state->delete('entity_test_update.additional_field_storage_definitions'); - $this->state->delete('entity_test_update.additional_bundle_field_definitions.test_bundle'); + $this->state->delete('entity_test_update.additional_bundle_field_definitions.entity_test_update'); } /** diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php index 686bc61514..49b895be1d 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionUpdateTest.php @@ -11,7 +11,6 @@ use Drupal\Core\Entity\EntityTypeEvents; use Drupal\Core\Entity\Exception\FieldStorageDefinitionUpdateForbiddenException; use Drupal\Core\Field\BaseFieldDefinition; -use Drupal\Core\Field\FieldDefinition; use Drupal\Core\Field\FieldException; use Drupal\Core\Field\FieldStorageDefinitionEvents; use Drupal\Core\Language\LanguageInterface; @@ -572,10 +571,11 @@ public function testBundleFieldDeleteWithExistingData() { // Add the bundle field and run the update. $this->addBundleField(); $this->entityDefinitionUpdateManager->applyUpdates(); + $field_definition = $this->entityManager->getFieldDefinitions('entity_test_update', 'entity_test_update')['new_bundle_field']; + $storage_definition = $field_definition->getFieldStorageDefinition(); /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ $table_mapping = $storage->getTableMapping(); - $storage_definition = $this->entityManager->getLastInstalledFieldStorageDefinitions('entity_test_update')['new_bundle_field']; // Check that the bundle field has a dedicated table. $dedicated_table_name = $table_mapping->getDedicatedDataTableName($storage_definition); @@ -583,7 +583,7 @@ public function testBundleFieldDeleteWithExistingData() { // Save an entity with the bundle field populated. entity_test_create_bundle('custom'); - $entity = $storage->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo']); + $entity = $storage->create(['type' => 'entity_test_update', 'new_bundle_field' => 'foo']); $entity->save(); // Remove the bundle field and apply updates. @@ -620,8 +620,7 @@ public function testBundleFieldDeleteWithExistingData() { // Check that the field definition is marked for purging. $deleted_field_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldDefinitions(); - $installed_field_definition = FieldDefinition::createFromFieldStorageDefinition($storage_definition)->setTargetBundle('test_bundle'); - $this->assertArrayHasKey($installed_field_definition->getUniqueIdentifier(), $deleted_field_definitions, 'The bundle field is marked for purging.'); + $this->assertArrayHasKey($field_definition->getUniqueIdentifier(), $deleted_field_definitions, 'The bundle field is marked for purging.'); // Check that the field storage definition is marked for purging. $deleted_storage_definitions = \Drupal::service('entity_field.deleted_fields_repository')->getFieldStorageDefinitions(); @@ -669,8 +668,7 @@ public function testBundleFieldUpdateWithExistingData() { $this->entityDefinitionUpdateManager->applyUpdates(); // Save an entity with the bundle field populated. - entity_test_create_bundle('custom'); - $this->entityManager->getStorage('entity_test_update')->create(['type' => 'test_bundle', 'new_bundle_field' => 'foo'])->save(); + $this->entityManager->getStorage('entity_test_update')->create(['type' => 'entity_test_update', 'new_bundle_field' => 'foo'])->save(); // Change the field's field type and apply updates. It's expected to // throw an exception. diff --git a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php index 9d4e4ad408..65a5f3045c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/FieldDefinitionTest.php @@ -4,6 +4,7 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\EntityFieldManagerInterface; +use Drupal\Core\Field\DeletedFieldsRepositoryInterface; use Drupal\Core\Field\FieldDefinition; use Drupal\Core\Field\FieldStorageDefinitionInterface; use Drupal\Core\Field\FieldTypePluginManagerInterface; @@ -67,10 +68,15 @@ protected function setUp() { $field_type_manager->getDefaultFieldSettings($this->fieldType)->willReturn($this->fieldTypeDefinition['field_settings']); $field_type_manager->getDefaultStorageSettings($this->fieldType)->willReturn($this->fieldTypeDefinition['storage_settings']); + $deleted_fields_repository = $this->prophesize(DeletedFieldsRepositoryInterface::class); + $deleted_fields_repository->getFieldDefinitions()->willReturn([]); + $deleted_fields_repository->getFieldStorageDefinitions()->willReturn([]); + $storage_definition = $this->prophesize(FieldStorageDefinitionInterface::class); $storage_definition->getMainPropertyName()->willReturn('value'); $storage_definition->getType()->willReturn($this->fieldType); $storage_definition->getName()->willReturn('test_field_name'); + $storage_definition->getUniqueStorageIdentifier()->willReturn('foo'); $storage_supports_translation = &$this->storageSupportsTranslation; $storage_definition->isTranslatable()->will(function () use (&$storage_supports_translation) { return $storage_supports_translation; @@ -91,6 +97,7 @@ protected function setUp() { $container->set('plugin.manager.field.field_type', $field_type_manager->reveal()); $container->set('entity_field.manager', $entity_field_manager->reveal()); $container->set('typed_data_manager', $typed_data_manager->reveal()); + $container->set('entity_field.deleted_fields_repository', $deleted_fields_repository->reveal()); \Drupal::setContainer($container); }