Commit 79630716 authored by plach's avatar plach

Issue #2960046 by amateescu, tstoeckler: Allow the table mapping to be...

Issue #2960046 by amateescu, tstoeckler: Allow the table mapping to be instantiated with a custom entity type definition
parent 95a478f3
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
use Drupal\Core\Database\SchemaException; use Drupal\Core\Database\SchemaException;
use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\ContentEntityStorageBase; use Drupal\Core\Entity\ContentEntityStorageBase;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityBundleListenerInterface; use Drupal\Core\Entity\EntityBundleListenerInterface;
use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityManagerInterface;
...@@ -305,27 +306,41 @@ public function setTemporary($temporary) { ...@@ -305,27 +306,41 @@ public function setTemporary($temporary) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function getTableMapping(array $storage_definitions = NULL) { public function getTableMapping(array $storage_definitions = NULL) {
$table_mapping = $this->tableMapping; // If a new set of field storage definitions is passed, for instance when
// comparing old and new storage schema, we compute the table mapping
// without caching.
if ($storage_definitions) {
return $this->getCustomTableMapping($this->entityType, $storage_definitions);
}
// If we are using our internal storage definitions, which is our main use // If we are using our internal storage definitions, which is our main use
// case, we can statically cache the computed table mapping. If a new set // case, we can statically cache the computed table mapping.
// of field storage definitions is passed, for instance when comparing old if (!isset($this->tableMapping)) {
// and new storage schema, we compute the table mapping without caching. $storage_definitions = $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
if (!isset($this->tableMapping) || $storage_definitions) {
$table_mapping_class = $this->temporary ? TemporaryTableMapping::class : DefaultTableMapping::class;
$definitions = $storage_definitions ?: $this->entityManager->getFieldStorageDefinitions($this->entityTypeId);
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping|\Drupal\Core\Entity\Sql\TemporaryTableMapping $table_mapping */
$table_mapping = $table_mapping_class::create($this->entityType, $definitions);
// Cache the computed table mapping only if we are using our internal $this->tableMapping = $this->getCustomTableMapping($this->entityType, $storage_definitions);
// storage definitions.
if (!$storage_definitions) {
$this->tableMapping = $table_mapping;
} }
return $this->tableMapping;
} }
return $table_mapping; /**
* Gets a table mapping for the specified entity type and storage definitions.
*
* @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type
* An entity type definition.
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $storage_definitions
* An array of field storage definitions to be used to compute the table
* mapping.
*
* @return \Drupal\Core\Entity\Sql\TableMappingInterface
* A table mapping object for the entity's tables.
*
* @internal
*/
public function getCustomTableMapping(ContentEntityTypeInterface $entity_type, array $storage_definitions) {
$table_mapping_class = $this->temporary ? TemporaryTableMapping::class : DefaultTableMapping::class;
return $table_mapping_class::create($entity_type, $storage_definitions);
} }
/** /**
...@@ -1397,14 +1412,6 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) { ...@@ -1397,14 +1412,6 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
* {@inheritdoc} * {@inheritdoc}
*/ */
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) { public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
// If we are adding a field stored in a shared table we need to recompute
// the table mapping.
// @todo This does not belong here. Remove it once we are able to generate a
// fresh table mapping in the schema handler. See
// https://www.drupal.org/node/2274017.
if ($this->getTableMapping()->allowsSharedTableStorage($storage_definition)) {
$this->tableMapping = NULL;
}
$this->wrapSchemaException(function () use ($storage_definition) { $this->wrapSchemaException(function () use ($storage_definition) {
$this->getStorageSchema()->onFieldStorageDefinitionCreate($storage_definition); $this->getStorageSchema()->onFieldStorageDefinitionCreate($storage_definition);
}); });
...@@ -1629,16 +1636,7 @@ public function countFieldData($storage_definition, $as_bool = FALSE) { ...@@ -1629,16 +1636,7 @@ public function countFieldData($storage_definition, $as_bool = FALSE) {
elseif ($table_mapping->allowsSharedTableStorage($storage_definition)) { elseif ($table_mapping->allowsSharedTableStorage($storage_definition)) {
// Ascertain the table this field is mapped too. // Ascertain the table this field is mapped too.
$field_name = $storage_definition->getName(); $field_name = $storage_definition->getName();
try {
$table_name = $table_mapping->getFieldTableName($field_name); $table_name = $table_mapping->getFieldTableName($field_name);
}
catch (SqlContentEntityStorageException $e) {
// This may happen when changing field storage schema, since we are not
// able to use a table mapping matching the passed storage definition.
// @todo Revisit this once we are able to instantiate the table mapping
// properly. See https://www.drupal.org/node/2274017.
$table_name = $this->dataTable ?: $this->baseTable;
}
$query = $this->database->select($table_name, 't'); $query = $this->database->select($table_name, 't');
$or = $query->orConditionGroup(); $or = $query->orConditionGroup();
foreach (array_keys($storage_definition->getColumns()) as $property_name) { foreach (array_keys($storage_definition->getColumns()) as $property_name) {
......
...@@ -388,36 +388,21 @@ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeI ...@@ -388,36 +388,21 @@ public function onEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeI
public function onEntityTypeDelete(EntityTypeInterface $entity_type) { public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
$this->checkEntityType($entity_type); $this->checkEntityType($entity_type);
$schema_handler = $this->database->schema(); $schema_handler = $this->database->schema();
$actual_definition = $this->entityManager->getDefinition($entity_type->id());
// @todo Instead of switching the wrapped entity type, we should be able to $field_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type->id());
// instantiate a new table mapping for each entity type definition. See $table_mapping = $this->storage->getCustomTableMapping($entity_type, $field_storage_definitions);
// https://www.drupal.org/node/2274017.
$this->storage->setEntityType($entity_type); // Delete entity and field tables.
foreach ($table_mapping->getTableNames() as $table_name) {
// Delete entity tables.
foreach ($this->getEntitySchemaTables() as $table_name) {
if ($schema_handler->tableExists($table_name)) { if ($schema_handler->tableExists($table_name)) {
$schema_handler->dropTable($table_name); $schema_handler->dropTable($table_name);
} }
} }
// Delete dedicated field tables. // Delete the field schema data.
$field_storage_definitions = $this->entityManager->getLastInstalledFieldStorageDefinitions($entity_type->id());
$this->originalDefinitions = $field_storage_definitions;
$table_mapping = $this->storage->getTableMapping($field_storage_definitions);
foreach ($field_storage_definitions as $field_storage_definition) { foreach ($field_storage_definitions as $field_storage_definition) {
// If we have a field having dedicated storage we need to drop it,
// otherwise we just remove the related schema data.
if ($table_mapping->requiresDedicatedTableStorage($field_storage_definition)) {
$this->deleteDedicatedTableSchema($field_storage_definition);
}
elseif ($table_mapping->allowsSharedTableStorage($field_storage_definition)) {
$this->deleteFieldSchemaData($field_storage_definition); $this->deleteFieldSchemaData($field_storage_definition);
} }
}
$this->originalDefinitions = NULL;
$this->storage->setEntityType($actual_definition);
// Delete the entity schema. // Delete the entity schema.
$this->deleteEntitySchemaData($entity_type); $this->deleteEntitySchemaData($entity_type);
...@@ -680,13 +665,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res ...@@ -680,13 +665,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res
$entity_type_id = $entity_type->id(); $entity_type_id = $entity_type->id();
if (!isset($this->schema[$entity_type_id]) || $reset) { if (!isset($this->schema[$entity_type_id]) || $reset) {
// Back up the storage definition and replace it with the passed one.
// @todo Instead of switching the wrapped entity type, we should be able
// to instantiate a new table mapping for each entity type definition.
// See https://www.drupal.org/node/2274017.
$actual_definition = $this->entityManager->getDefinition($entity_type_id);
$this->storage->setEntityType($entity_type);
// Prepare basic information about the entity type. // Prepare basic information about the entity type.
$tables = $this->getEntitySchemaTables(); $tables = $this->getEntitySchemaTables();
...@@ -703,7 +681,7 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res ...@@ -703,7 +681,7 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res
} }
// We need to act only on shared entity schema tables. // We need to act only on shared entity schema tables.
$table_mapping = $this->storage->getTableMapping(); $table_mapping = $this->storage->getCustomTableMapping($entity_type, $this->fieldStorageDefinitions);
$table_names = array_diff($table_mapping->getTableNames(), $table_mapping->getDedicatedTableNames()); $table_names = array_diff($table_mapping->getTableNames(), $table_mapping->getDedicatedTableNames());
foreach ($table_names as $table_name) { foreach ($table_names as $table_name) {
if (!isset($schema[$table_name])) { if (!isset($schema[$table_name])) {
...@@ -753,9 +731,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res ...@@ -753,9 +731,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res
} }
$this->schema[$entity_type_id] = $schema; $this->schema[$entity_type_id] = $schema;
// Restore the actual definition.
$this->storage->setEntityType($actual_definition);
} }
return $this->schema[$entity_type_id]; return $this->schema[$entity_type_id];
......
...@@ -370,6 +370,9 @@ public function testGetSchemaBase() { ...@@ -370,6 +370,9 @@ public function testGetSchemaBase() {
$this->storage->expects($this->any()) $this->storage->expects($this->any())
->method('getTableMapping') ->method('getTableMapping')
->will($this->returnValue($table_mapping)); ->will($this->returnValue($table_mapping));
$this->storage->expects($this->any())
->method('getCustomTableMapping')
->will($this->returnValue($table_mapping));
$this->assertNull( $this->assertNull(
$this->storageSchema->onEntityTypeCreate($this->entityType) $this->storageSchema->onEntityTypeCreate($this->entityType)
...@@ -478,6 +481,9 @@ public function testGetSchemaRevisionable() { ...@@ -478,6 +481,9 @@ public function testGetSchemaRevisionable() {
$this->storage->expects($this->any()) $this->storage->expects($this->any())
->method('getTableMapping') ->method('getTableMapping')
->will($this->returnValue($table_mapping)); ->will($this->returnValue($table_mapping));
$this->storage->expects($this->any())
->method('getCustomTableMapping')
->will($this->returnValue($table_mapping));
$this->storageSchema->onEntityTypeCreate($this->entityType); $this->storageSchema->onEntityTypeCreate($this->entityType);
} }
...@@ -586,6 +592,9 @@ public function testGetSchemaTranslatable() { ...@@ -586,6 +592,9 @@ public function testGetSchemaTranslatable() {
$this->storage->expects($this->any()) $this->storage->expects($this->any())
->method('getTableMapping') ->method('getTableMapping')
->will($this->returnValue($table_mapping)); ->will($this->returnValue($table_mapping));
$this->storage->expects($this->any())
->method('getCustomTableMapping')
->will($this->returnValue($table_mapping));
$this->assertNull( $this->assertNull(
$this->storageSchema->onEntityTypeCreate($this->entityType) $this->storageSchema->onEntityTypeCreate($this->entityType)
...@@ -806,6 +815,9 @@ public function testGetSchemaRevisionableTranslatable() { ...@@ -806,6 +815,9 @@ public function testGetSchemaRevisionableTranslatable() {
$this->storage->expects($this->any()) $this->storage->expects($this->any())
->method('getTableMapping') ->method('getTableMapping')
->will($this->returnValue($table_mapping)); ->will($this->returnValue($table_mapping));
$this->storage->expects($this->any())
->method('getCustomTableMapping')
->will($this->returnValue($table_mapping));
$this->storageSchema->onEntityTypeCreate($this->entityType); $this->storageSchema->onEntityTypeCreate($this->entityType);
} }
...@@ -1303,6 +1315,9 @@ public function testRequiresEntityStorageSchemaChanges(ContentEntityTypeInterfac ...@@ -1303,6 +1315,9 @@ public function testRequiresEntityStorageSchemaChanges(ContentEntityTypeInterfac
$this->storage->expects($this->any()) $this->storage->expects($this->any())
->method('getTableMapping') ->method('getTableMapping')
->will($this->returnValue($table_mapping)); ->will($this->returnValue($table_mapping));
$this->storage->expects($this->any())
->method('getCustomTableMapping')
->will($this->returnValue($table_mapping));
// Setup storage schema. // Setup storage schema.
if ($change_schema) { if ($change_schema) {
...@@ -1487,6 +1502,9 @@ public function testonEntityTypeUpdateWithNewIndex() { ...@@ -1487,6 +1502,9 @@ public function testonEntityTypeUpdateWithNewIndex() {
$this->storage->expects($this->any()) $this->storage->expects($this->any())
->method('getTableMapping') ->method('getTableMapping')
->will($this->returnValue($table_mapping)); ->will($this->returnValue($table_mapping));
$this->storage->expects($this->any())
->method('getCustomTableMapping')
->will($this->returnValue($table_mapping));
$this->storageSchema->expects($this->any()) $this->storageSchema->expects($this->any())
->method('loadEntitySchemaData') ->method('loadEntitySchemaData')
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment