.../Core/Entity/ContentEntityNullStorage.php | 7 +++ .../DynamicallyFieldableEntityStorageInterface.php | 7 +++ core/lib/Drupal/Core/Entity/EntityManager.php | 33 ++++++----- .../Drupal/Core/Entity/EntityManagerInterface.php | 19 ++++++- .../Core/Entity/Sql/SqlContentEntityStorage.php | 11 ++++ .../Entity/Sql/SqlContentEntityStorageSchema.php | 25 ++++----- .../Entity/Sql/SqlContentEntityStorageTest.php | 65 ++++++++++++++++++++++ 7 files changed, 138 insertions(+), 29 deletions(-) diff --git a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php index 12aeca3..f31b80e 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityNullStorage.php @@ -138,4 +138,11 @@ public function countFieldData($storage_definition, $as_bool = FALSE) { return $as_bool ? FALSE : 0; } + /** + * {@inheritdoc} + */ + public function hasData() { + return FALSE; + } + } diff --git a/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php b/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php index 6d12c98..1d20217 100644 --- a/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php +++ b/core/lib/Drupal/Core/Entity/DynamicallyFieldableEntityStorageInterface.php @@ -85,6 +85,13 @@ public function purgeFieldData(FieldDefinitionInterface $field_definition, $batc public function countFieldData($storage_definition, $as_bool = FALSE); /** + * Determines if the storage contains any data. + * + * @return bool + */ + public function hasData(); + + /** * Performs final cleanup after all data of a field has been purged. * * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 128d21d..7d7eded 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -324,19 +324,7 @@ public function getHandler($entity_type, $handler_type) { if (!$class) { throw new InvalidPluginDefinitionException($entity_type, sprintf('The "%s" entity type did not specify a %s handler.', $entity_type, $handler_type)); } - if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) { - $handler = $class::createInstance($this->container, $definition); - } - else { - $handler = new $class($definition); - } - if (method_exists($handler, 'setModuleHandler')) { - $handler->setModuleHandler($this->moduleHandler); - } - if (method_exists($handler, 'setStringTranslation')) { - $handler->setStringTranslation($this->translationManager); - } - $this->handlers[$handler_type][$entity_type] = $handler; + $this->handlers[$handler_type][$entity_type] = $this->createHandlerInstance($class, $definition); } return $this->handlers[$handler_type][$entity_type]; } @@ -344,6 +332,25 @@ public function getHandler($entity_type, $handler_type) { /** * {@inheritdoc} */ + public function createHandlerInstance($class, EntityTypeInterface $definition = null) { + if (is_subclass_of($class, 'Drupal\Core\Entity\EntityHandlerInterface')) { + $handler = $class::createInstance($this->container, $definition); + } + else { + $handler = new $class($definition); + } + if (method_exists($handler, 'setModuleHandler')) { + $handler->setModuleHandler($this->moduleHandler); + } + if (method_exists($handler, 'setStringTranslation')) { + $handler->setStringTranslation($this->translationManager); + } + return $handler; + } + + /** + * {@inheritdoc} + */ public function getBaseFieldDefinitions($entity_type_id) { // Check the static cache. if (!isset($this->baseFieldDefinitions[$entity_type_id])) { diff --git a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php index 8cf95ee..a1dea1d 100644 --- a/core/lib/Drupal/Core/Entity/EntityManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityManagerInterface.php @@ -237,7 +237,7 @@ public function getFormObject($entity_type, $operation); public function hasHandler($entity_type, $handler_type); /** - * Creates a new handler instance. + * Creates a new handler instance for a entity type and handler type. * * @param string $entity_type * The entity type for this controller. @@ -252,6 +252,23 @@ public function hasHandler($entity_type, $handler_type); public function getHandler($entity_type, $handler_type); /** + * Creates new handler instance. + * + * Usually \Drupal\Core\Entity\EntityManagerInterface::getHandler() is + * preferred since that method has additional checking that the class exists + * and has static caches. + * + * @param mixed $class + * The handler class to instantiate. + * @param \Drupal\Core\Entity\EntityTypeInterface $definition + * The entity type definition. + * + * @return mixed + * A handler instance. + */ + public function createHandlerInstance($class, EntityTypeInterface $definition = null); + + /** * Get the bundle info of an entity type. * * @param string $entity_type diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index b8db248..2ace5c7 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -1765,6 +1765,17 @@ public function countFieldData($storage_definition, $as_bool = FALSE) { } /** + * {@inheritdoc} + */ + public function hasData() { + return (bool) $this->database->select($this->getBaseTable(), 'base') + ->fields('base') + ->countQuery() + ->execute() + ->fetchField(); + } + + /** * Returns whether the passed field has been already deleted. * * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index dac6584..36459b7 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -178,23 +178,18 @@ public function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterfac * {@inheritdoc} */ public function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original) { - // If we're updating from NULL storage, then there's no stored data that - // requires migration. - // @todo Remove in https://www.drupal.org/node/2335879. $original_storage_class = $original->getStorageClass(); - $null_storage_class = 'Drupal\Core\Entity\ContentEntityNullStorage'; - if ($original_storage_class == $null_storage_class || is_subclass_of($original_storage_class, $null_storage_class)) { - return FALSE; - } - return - // If the original storage class is different, then there might be - // existing entities in that storage even if the new storage's base - // table is empty. - // @todo Ask the old storage handler rather than assuming: - // https://www.drupal.org/node/2335879. - $entity_type->getStorageClass() != $original_storage_class || - !$this->isTableEmpty($this->storage->getBaseTable()); + // If the original storage class is different, then there might be existing + // entities in that storage even if the new storage's base table is empty. + if ($entity_type->getStorageClass() != $original_storage_class) { + if (!class_exists($original_storage_class)) { + return TRUE; + } + $original_storage = $this->entityManager->createHandlerInstance($original_storage_class, $original); + return $original_storage->hasData(); + } + return $this->storage->hasData(); } /** diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index 3589a63..5abcaaf 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -1181,6 +1181,71 @@ public function testLoadMultiplePersistentCacheMiss() { } /** + * @covers ::hasData + */ + public function testHasData() { + $select_fetch_field = $this->getMockBuilder('\Drupal\Core\Database\StatementInterface') + ->disableOriginalConstructor() + ->getMock(); + + $select_fetch_field->expects($this->once()) + ->method('fetchField') + ->will($this->returnValue(TRUE)); + + $select_execute = $this->getMockBuilder('\Drupal\Core\Database\Query\Select') + ->disableOriginalConstructor() + ->getMock(); + + $select_execute->expects($this->once()) + ->method('execute') + ->will($this->returnValue($select_fetch_field)); + + $select_count_query = $this->getMockBuilder('\Drupal\Core\Database\Query\Select') + ->disableOriginalConstructor() + ->getMock(); + + $select_count_query->expects($this->once()) + ->method('countQuery') + ->will($this->returnValue($select_execute)); + + $database_select = $this->getMockBuilder('\Drupal\Core\Database\Query\Select') + ->disableOriginalConstructor() + ->getMock(); + + $database_select->expects($this->once()) + ->method('fields') + ->will($this->returnValue($select_count_query)); + + $database = $this->getMockBuilder('Drupal\Core\Database\Connection') + ->disableOriginalConstructor() + ->getMock(); + + $database->expects($this->once()) + ->method('select') + ->will($this->returnValue($database_select)); + + $this->container->set('database', $database); + + $this->entityManager->expects($this->any()) + ->method('getDefinition') + ->will($this->returnValue($this->entityType)); + + $this->entityManager->expects($this->any()) + ->method('getFieldStorageDefinitions') + ->will($this->returnValue($this->fieldDefinitions)); + + $this->entityManager->expects($this->any()) + ->method('getBaseFieldDefinitions') + ->will($this->returnValue($this->fieldDefinitions)); + + $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityManager, $this->cache); + + $result = $this->entityStorage->hasData(); + + $this->assertTrue($result, 'hasData returned TRUE'); + } + + /** * Tests entity ID sanitization. */ public function testCleanIds() {