diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php index e6fe5fb..d09ecfd 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityDatabaseStorage.php @@ -19,7 +19,6 @@ use Drupal\Core\Language\Language; use Drupal\Core\Entity\Exception\StorageDefinitionUpdateForbiddenException; use Drupal\field\Entity\FieldConfig; -use Drupal\field\FieldInstanceConfigInterface; use Symfony\Component\DependencyInjection\ContainerInterface; /** @@ -952,7 +951,7 @@ protected function doLoadFieldItems($entities, $age) { $fields = array(); foreach ($bundles as $bundle => $v) { foreach ($this->entityManager->getFieldDefinitions($this->entityTypeId, $bundle) as $field_name => $instance) { - if ($instance instanceof FieldInstanceConfigInterface) { + if ($this->usesDedicatedTable($instance)) { $fields[$field_name] = $instance; } } @@ -1019,7 +1018,7 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { } foreach ($this->entityManager->getFieldDefinitions($entity_type, $bundle) as $field_name => $instance) { - if (!($instance instanceof FieldInstanceConfigInterface)) { + if (!$this->usesDedicatedTable($instance)) { continue; } $table_name = static::_fieldTableName($instance); @@ -1095,7 +1094,7 @@ protected function doSaveFieldItems(EntityInterface $entity, $update) { */ protected function doDeleteFieldItems(EntityInterface $entity) { foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) { - if (!($instance instanceof FieldInstanceConfigInterface)) { + if (!$this->usesDedicatedTable($instance)) { continue; } $table_name = static::_fieldTableName($instance); @@ -1116,7 +1115,7 @@ protected function doDeleteFieldItemsRevision(EntityInterface $entity) { $vid = $entity->getRevisionId(); if (isset($vid)) { foreach ($this->entityManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $instance) { - if (!($instance instanceof FieldInstanceConfigInterface)) { + if (!$this->usesDedicatedTable($instance)) { continue; } $revision_name = static::_fieldRevisionTableName($instance); @@ -1129,6 +1128,21 @@ protected function doDeleteFieldItemsRevision(EntityInterface $entity) { } /** + * Returns whether the field uses a dedicated table for storage. + * + * @param FieldStorageDefinitionInterface $definition + * The field storage definition. + * + * @return bool + * Whether the field uses a dedicated table for storage. + */ + protected function usesDedicatedTable(FieldStorageDefinitionInterface $definition) { + // Everything that is not provided by the entity type is stored in a + // dedicated table. + return $definition->getProvider() != $this->entityType->getProvider(); + } + + /** * {@inheritdoc} */ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) { @@ -1240,6 +1254,9 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $ $revision_new_table = static::_fieldRevisionTableName($storage_definition, TRUE); $this->database->schema()->renameTable($table, $new_table); $this->database->schema()->renameTable($revision_table, $revision_new_table); + + // Make sure any deleted field definitions disappear. + $this->entityManager->clearCachedFieldDefinitions(); } /** diff --git a/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php new file mode 100644 index 0000000..16521c0 --- /dev/null +++ b/core/modules/system/src/Tests/Entity/EntityBundleFieldTest.php @@ -0,0 +1,102 @@ + 'Entity bundle fields', + 'description' => 'Tests providing a custom bundle field.', + 'group' => 'Entity API', + ); + } + + /** + * {@inheritdoc} + */ + public function setUp() { + parent::setUp(); + $this->installSchema('user', array('users_data')); + $this->installSchema('system', array('router')); + $this->moduleHandler = $this->container->get('module_handler'); + $this->database = $this->container->get('database'); + } + + /** + * Tests the custom bundle field creation and deletion. + */ + public function testCustomBundleFieldCreateDelete() { + // Install the module which adds the field. + $this->moduleHandler->install(array('entity_bundle_field_test'), FALSE); + $definition = $this->entityManager->getFieldDefinitions('entity_test', 'custom')['custom_field']; + $this->assertNotNull($definition, 'Field definition found.'); + // Make sure the table has been created. + $table = $this->entityManager->getStorage('entity_test')->_fieldTableName($definition); + + $this->assertTrue($this->database->schema()->tableExists($table), 'Table created'); + $this->moduleHandler->uninstall(array('entity_bundle_field_test'), FALSE); + $this->assertFalse($this->database->schema()->tableExists($table), 'Table dropped'); + } + + /** + * Tests making use of a custom bundle field. + */ + public function testCustomBundleFieldUsage() { + $this->moduleHandler->install(array('entity_bundle_field_test'), FALSE); + $storage = $this->entityManager->getStorage('entity_test'); + $entity = $storage->create([ + 'type' => 'entity_test', + ]); + $this->assertFalse($entity->hasField('custom_field')); + $entity = $storage->create([ + 'type' => 'custom', + ]); + $this->assertTrue($entity->hasField('custom_field')); + $entity->custom_field->value = 'swanky'; + $entity->save(); + $storage->resetCache(); + $entity = $storage->load($entity->id()); + $this->assertEqual($entity->custom_field->value, 'swanky'); + + $entity->custom_field->value = 'cozy'; + $entity->save(); + $storage->resetCache(); + $entity = $storage->load($entity->id()); + $this->assertEqual($entity->custom_field->value, 'cozy'); + $entity->delete(); + + $table = $storage->_fieldTableName($entity->getFieldDefinition('custom_field')); + $result = $this->database->select($table, 'f') + ->fields('f') + ->condition('entity_id', $entity->id()) + ->execute(); + $this->assertFalse($result->fetchAssoc(), 'Field data has been deleted'); + } + +} diff --git a/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.info.yml b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.info.yml new file mode 100644 index 0000000..eca979e --- /dev/null +++ b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.info.yml @@ -0,0 +1,8 @@ +name: 'Entity bundle field test module' +type: module +description: 'Provides a bundle field to the test entity.' +package: Testing +version: VERSION +core: 8.x +dependencies: + - entity_test \ No newline at end of file diff --git a/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.install b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.install new file mode 100644 index 0000000..8779878 --- /dev/null +++ b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.install @@ -0,0 +1,40 @@ +getFieldStorageDefinitions('entity_test')['custom_field']; + $manager->getStorage('entity_test')->onFieldStorageDefinitionCreate($definition); + + // Create the custom bundle and put our bundle field on it. + entity_test_create_bundle('custom'); + $definition = $manager->getFieldDefinitions('entity_test', 'custom')['custom_field']; + $manager->getStorage('entity_test')->onFieldDefinitionCreate($definition); +} + +/** + * Implements hook_uninstall(). + */ +function entity_bundle_field_test_uninstall() { + entity_bundle_field_test_is_uninstalling(TRUE); + $manager = \Drupal::entityManager(); + // Notify the entity storage that our field is gone. + $definition = $manager->getFieldDefinitions('entity_test', 'custom')['custom_field']; + $manager->getStorage('entity_test')->onFieldDefinitionDelete($definition); + $definition = $manager->getFieldStorageDefinitions('entity_test')['custom_field']; + $manager->getStorage('entity_test')->onFieldStorageDefinitionDelete($definition); + + do { + $count = $manager->getStorage('entity_test')->purgeFieldData($definition, 500); + } + while ($count != 0); + $manager->getStorage('entity_test')->finalizePurge($definition); +} diff --git a/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.module b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.module new file mode 100644 index 0000000..81e9df4 --- /dev/null +++ b/core/modules/system/tests/modules/entity_bundle_field_test/entity_bundle_field_test.module @@ -0,0 +1,66 @@ +id() == 'entity_test' && !entity_bundle_field_test_is_uninstalling()) { + // @todo: Make use of a FieldStorageDefinition class instead of + // FieldDefinition as this should not implement FieldDefinitionInterface. + // See https://drupal.org/node/2280639. + $definitions['custom_field'] = FieldDefinition::create('string') + ->setName('custom_field') + ->setLabel(t('A custom field')) + ->setTargetEntityTypeId($entity_type->id()); + return $definitions; + } +} + +/** + * Implements hook_entity_bundle_field_info(). + */ +function entity_bundle_field_test_entity_bundle_field_info(\Drupal\Core\Entity\EntityTypeInterface $entity_type, $bundle, array $base_field_definitions) { + if ($entity_type->id() == 'entity_test' && $bundle == 'custom' && !entity_bundle_field_test_is_uninstalling()) { + $definitions['custom_field'] = FieldDefinition::create('string') + ->setName('custom_field') + ->setLabel(t('A custom field')); + return $definitions; + } +} + +/** + * Implements hook_entity_bundle_delete(). + */ +function entity_bundle_field_test_entity_bundle_delete($entity_type, $bundle) { + if ($entity_type->id() == 'entity_test' && $bundle == 'custom') { + // Notify the entity storage that our field is gone. + \Drupal::entityManager()->getStorage('entity_test') + >onFieldDefinitionDelete(FieldDefinition::create('string') + ->setName('custom_field') + ->setLabel(t('A custom field'))); + } +}