Commit 5d3322eb authored by plach's avatar plach

Issue #2554235 by amateescu, plach, Berdir, Sam152, joelpittet, mbaynton,...

Issue #2554235 by amateescu, plach, Berdir, Sam152, joelpittet, mbaynton, catch, jibran: Make the content entity storage and entity query use the last installed definitions instead of the ones living in code
parent 0b47db51
......@@ -552,7 +552,7 @@ services:
class: Drupal\Core\Cache\MemoryCache\MemoryCache
entity_type.manager:
class: Drupal\Core\Entity\EntityTypeManager
arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@string_translation', '@class_resolver']
arguments: ['@container.namespaces', '@module_handler', '@cache.discovery', '@string_translation', '@class_resolver', '@entity.last_installed_schema.repository']
parent: container.trait
tags:
- { name: plugin_manager_cache_clear }
......
......@@ -985,7 +985,7 @@ protected function populateAffectedRevisionTranslations(ContentEntityInterface $
* The sanitized list of entity key values.
*/
protected function cleanIds(array $ids, $entity_key = 'id') {
$definitions = $this->entityFieldManager->getBaseFieldDefinitions($this->entityTypeId);
$definitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($this->entityTypeId);
$field_name = $this->entityType->getKey($entity_key);
if ($field_name && $definitions[$field_name]->getType() == 'integer') {
$ids = array_filter($ids, function ($id) {
......
......@@ -227,6 +227,7 @@ public function updateFieldableEntityType(EntityTypeInterface $entity_type, arra
$original_field_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type->id());
$this->entityTypeListener->onFieldableEntityTypeUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox);
$this->clearCachedDefinitions();
}
/**
......
......@@ -55,6 +55,13 @@ class EntityFieldManager implements EntityFieldManagerInterface {
*/
protected $fieldStorageDefinitions;
/**
* Static cache of active field storage definitions per entity type.
*
* @var array
*/
protected $activeFieldStorageDefinitions;
/**
* An array keyed by entity type. Each value is an array whose keys are
* field names and whose value is an array with two entries:
......@@ -445,6 +452,25 @@ public function getFieldStorageDefinitions($entity_type_id) {
return $this->fieldStorageDefinitions[$entity_type_id];
}
/**
* Gets the active field storage definitions for a content entity type.
*
* @param string $entity_type_id
* The entity type ID. Only content entities are supported.
*
* @return \Drupal\Core\Field\FieldStorageDefinitionInterface[]
* An array of field storage definitions that are active in the current
* request, keyed by field name.
*
* @internal
*/
public function getActiveFieldStorageDefinitions($entity_type_id) {
if (!isset($this->activeFieldStorageDefinitions[$entity_type_id])) {
$this->activeFieldStorageDefinitions[$entity_type_id] = $this->keyValueFactory->get('entity.definitions.installed')->get($entity_type_id . '.field_storage_definitions', []);
}
return $this->activeFieldStorageDefinitions[$entity_type_id] ?: $this->getFieldStorageDefinitions($entity_type_id);
}
/**
* {@inheritdoc}
*/
......@@ -569,6 +595,7 @@ public function clearCachedFieldDefinitions() {
$this->baseFieldDefinitions = [];
$this->fieldDefinitions = [];
$this->fieldStorageDefinitions = [];
$this->activeFieldStorageDefinitions = [];
$this->fieldMap = [];
$this->fieldMapByFieldType = [];
$this->entityDisplayRepository->clearDisplayModeInfo();
......@@ -588,6 +615,7 @@ public function useCaches($use_caches = FALSE) {
$this->fieldDefinitions = [];
$this->baseFieldDefinitions = [];
$this->fieldStorageDefinitions = [];
$this->activeFieldStorageDefinitions = [];
}
}
......
......@@ -209,6 +209,20 @@ public function getFieldStorageDefinitions($entity_type_id) {
return $this->container->get('entity_field.manager')->getFieldStorageDefinitions($entity_type_id);
}
/**
* {@inheritdoc}
*
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
* Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions()
* instead.
*
* @see https://www.drupal.org/node/3040966
*/
public function getActiveFieldStorageDefinitions($entity_type_id) {
@trigger_error('EntityManagerInterface::getActiveFieldStorageDefinitions() is deprecated in 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions() instead. See https://www.drupal.org/node/3040966.', E_USER_DEPRECATED);
return $this->container->get('entity_field.manager')->getActiveFieldStorageDefinitions($entity_type_id);
}
/**
* {@inheritdoc}
*
......@@ -795,6 +809,20 @@ public function hasDefinition($plugin_id) {
return $this->container->get('entity_type.manager')->hasDefinition($plugin_id);
}
/**
* {@inheritdoc}
*
* @deprecated in Drupal 8.0.0, will be removed before Drupal 9.0.0.
* Use \Drupal\Core\Entity\EntityTypeManagerInterface::getActiveDefinition()
* instead.
*
* @see https://www.drupal.org/node/3040966
*/
public function getActiveDefinition($entity_type_id) {
@trigger_error('EntityManagerInterface::getActiveDefinition() is deprecated in 8.0.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityTypeManagerInterface::getActiveDefinition() instead. See https://www.drupal.org/node/3040966.', E_USER_DEPRECATED);
return $this->container->get('entity_type.manager')->getActiveDefinition($entity_type_id);
}
/**
* {@inheritdoc}
*
......
......@@ -58,6 +58,20 @@ class EntityTypeManager extends DefaultPluginManager implements EntityTypeManage
*/
protected $classResolver;
/**
* The entity last installed schema repository.
*
* @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
*/
protected $entityLastInstalledSchemaRepository;
/**
* A list of entity type definitions that are active for the current request.
*
* @var \Drupal\Core\Entity\EntityTypeInterface[]
*/
protected $activeDefinitions;
/**
* Constructs a new Entity plugin manager.
*
......@@ -72,8 +86,10 @@ class EntityTypeManager extends DefaultPluginManager implements EntityTypeManage
* The string translation.
* @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver
* The class resolver.
* @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
* The entity last installed schema repository.
*/
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, TranslationInterface $string_translation, ClassResolverInterface $class_resolver) {
public function __construct(\Traversable $namespaces, ModuleHandlerInterface $module_handler, CacheBackendInterface $cache, TranslationInterface $string_translation, ClassResolverInterface $class_resolver, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository) {
parent::__construct('Entity', $namespaces, $module_handler, 'Drupal\Core\Entity\EntityInterface');
$this->setCacheBackend($cache, 'entity_type', ['entity_types']);
......@@ -82,6 +98,7 @@ public function __construct(\Traversable $namespaces, ModuleHandlerInterface $mo
$this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
$this->stringTranslation = $string_translation;
$this->classResolver = $class_resolver;
$this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
}
/**
......@@ -133,11 +150,31 @@ public function getDefinition($entity_type_id, $exception_on_invalid = TRUE) {
throw new PluginNotFoundException($entity_type_id, sprintf('The "%s" entity type does not exist.', $entity_type_id));
}
/**
* Gets the active definition for a content entity type.
*
* @param string $entity_type_id
* The entity type ID.
*
* @return \Drupal\Core\Entity\EntityTypeInterface
* The active entity type definition.
*
* @internal
*/
public function getActiveDefinition($entity_type_id) {
if (!isset($this->activeDefinitions[$entity_type_id])) {
$this->activeDefinitions[$entity_type_id] = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id);
}
return $this->activeDefinitions[$entity_type_id] ?: $this->getDefinition($entity_type_id);
}
/**
* {@inheritdoc}
*/
public function clearCachedDefinitions() {
parent::clearCachedDefinitions();
$this->activeDefinitions = [];
$this->handlers = [];
}
......@@ -147,6 +184,7 @@ public function clearCachedDefinitions() {
public function useCaches($use_caches = FALSE) {
parent::useCaches($use_caches);
if (!$use_caches) {
$this->activeDefinitions = [];
$this->handlers = [];
$this->container->get('entity.memory_cache')->reset();
}
......
......@@ -3,6 +3,7 @@
namespace Drupal\Core\Entity\Query\Sql;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
use Drupal\Core\Entity\EntityType;
use Drupal\Core\Entity\Query\QueryException;
use Drupal\Core\Entity\Sql\SqlEntityStorageInterface;
......@@ -16,6 +17,13 @@
*/
class Tables implements TablesInterface {
use DeprecatedServicePropertyTrait;
/**
* {@inheritdoc}
*/
protected $deprecatedProperties = ['entityManager' => 'entity.manager'];
/**
* @var \Drupal\Core\Database\Query\SelectInterface
*/
......@@ -44,11 +52,18 @@ class Tables implements TablesInterface {
protected $fieldTables = [];
/**
* The entity manager.
* The entity type manager.
*
* @var \Drupal\Core\Entity\EntityTypeManagerInterface
*/
protected $entityTypeManager;
/**
* The entity field manager.
*
* @var \Drupal\Core\Entity\EntityManager
* @var \Drupal\Core\Entity\EntityFieldManagerInterface
*/
protected $entityManager;
protected $entityFieldManager;
/**
* List of case sensitive fields.
......@@ -62,7 +77,8 @@ class Tables implements TablesInterface {
*/
public function __construct(SelectInterface $sql_query) {
$this->sqlQuery = $sql_query;
$this->entityManager = \Drupal::entityManager();
$this->entityTypeManager = \Drupal::entityTypeManager();
$this->entityFieldManager = \Drupal::service('entity_field.manager');
}
/**
......@@ -85,9 +101,9 @@ public function addField($field, $type, $langcode) {
// This will contain the definitions of the last specifier seen by the
// system.
$propertyDefinitions = [];
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$entity_type = $this->entityTypeManager->getActiveDefinition($entity_type_id);
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
$field_storage_definitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($entity_type_id);
for ($key = 0; $key <= $count; $key++) {
// This can either be the name of an entity base field or a configurable
// field.
......@@ -118,7 +134,7 @@ public function addField($field, $type, $langcode) {
}
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
$table_mapping = $this->entityTypeManager->getStorage($entity_type_id)->getTableMapping();
// Check whether this field is stored in a dedicated table.
if ($field_storage && $table_mapping->requiresDedicatedTableStorage($field_storage)) {
......@@ -277,8 +293,8 @@ public function addField($field, $type, $langcode) {
if (!$entity_type_id && $target_definition instanceof EntityDataDefinitionInterface) {
$entity_type_id = $target_definition->getEntityTypeId();
}
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$field_storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id);
$entity_type = $this->entityTypeManager->getActiveDefinition($entity_type_id);
$field_storage_definitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($entity_type_id);
// Add the new entity base table using the table and sql column.
$base_table = $this->addNextBaseTable($entity_type, $table, $sql_column, $field_storage);
$propertyDefinitions = [];
......@@ -364,7 +380,7 @@ protected function ensureFieldTable($index_prefix, &$field, $type, $langcode, $b
if (!isset($this->fieldTables[$index_prefix . $field_name])) {
$entity_type_id = $this->sqlQuery->getMetaData('entity_type');
/** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */
$table_mapping = $this->entityManager->getStorage($entity_type_id)->getTableMapping();
$table_mapping = $this->entityTypeManager->getStorage($entity_type_id)->getTableMapping();
$table = !$this->sqlQuery->getMetaData('all_revisions') ? $table_mapping->getDedicatedDataTableName($field) : $table_mapping->getDedicatedRevisionTableName($field);
if ($field->getCardinality() != 1) {
$this->sqlQuery->addMetaData('simple_query', FALSE);
......@@ -395,7 +411,7 @@ protected function addJoin($type, $table, $join_condition, $langcode, $delta = N
$arguments = [];
if ($langcode) {
$entity_type_id = $this->sqlQuery->getMetaData('entity_type');
$entity_type = $this->entityManager->getDefinition($entity_type_id);
$entity_type = $this->entityTypeManager->getActiveDefinition($entity_type_id);
// Only the data table follows the entity language key, dedicated field
// tables have an hard-coded 'langcode' column.
$langcode_key = $entity_type->getDataTable() == $table ? $entity_type->getKey('langcode') : 'langcode';
......@@ -423,7 +439,7 @@ protected function addJoin($type, $table, $join_condition, $langcode, $delta = N
* mapping is not available, FALSE is returned.
*/
protected function getTableMapping($table, $entity_type_id) {
$storage = $this->entityManager->getStorage($entity_type_id);
$storage = $this->entityTypeManager->getStorage($entity_type_id);
if ($storage instanceof SqlEntityStorageInterface) {
$mapping = $storage->getTableMapping()->getAllColumns($table);
}
......
......@@ -14,7 +14,6 @@
use Drupal\Core\Entity\EntityBundleListenerInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
use Drupal\Core\Entity\EntityTypeBundleInfoInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityStorageException;
......@@ -42,6 +41,13 @@
*/
class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEntityStorageInterface, DynamicallyFieldableEntityStorageSchemaInterface, EntityBundleListenerInterface {
/**
* The entity type's field storage definitions.
*
* @var \Drupal\Core\Field\FieldStorageDefinitionInterface[]
*/
protected $fieldStorageDefinitions;
/**
* The mapping of field columns to SQL tables.
*
......@@ -128,13 +134,6 @@ class SqlContentEntityStorage extends ContentEntityStorageBase implements SqlEnt
*/
protected $entityTypeManager;
/**
* The entity last installed schema repository.
*
* @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
*/
protected $entityLastInstalledSchemaRepository;
/**
* Whether this storage should use the temporary table mapping.
*
......@@ -154,8 +153,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
$container->get('language_manager'),
$container->get('entity.memory_cache'),
$container->get('entity_type.bundle.info'),
$container->get('entity_type.manager'),
$container->get('entity.last_installed_schema.repository')
$container->get('entity_type.manager')
);
}
......@@ -165,8 +163,15 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
* @return \Drupal\Core\Field\FieldDefinitionInterface[]
* The array of base field definitions for the entity type, keyed by field
* name.
*
* @deprecated in Drupal 8.7.0, will be removed before Drupal 9.0.0.
* Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions()
* instead.
*
* @see https://www.drupal.org/node/3040966
*/
public function getFieldStorageDefinitions() {
@trigger_error('SqlContentEntityStorage::getFieldStorageDefinitions() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityFieldManagerInterface::getActiveFieldStorageDefinitions() instead. See https://www.drupal.org/node/3040966.', E_USER_DEPRECATED);
return $this->entityFieldManager->getBaseFieldDefinitions($this->entityTypeId);
}
......@@ -189,23 +194,18 @@ public function getFieldStorageDefinitions() {
* The entity type bundle info.
* @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager
* The entity type manager.
* @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
* The entity last installed schema repository.
*/
public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, MemoryCacheInterface $memory_cache = NULL, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, EntityTypeManagerInterface $entity_type_manager = NULL, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository = NULL) {
public function __construct(EntityTypeInterface $entity_type, Connection $database, EntityFieldManagerInterface $entity_field_manager, CacheBackendInterface $cache, LanguageManagerInterface $language_manager, MemoryCacheInterface $memory_cache = NULL, EntityTypeBundleInfoInterface $entity_type_bundle_info = NULL, EntityTypeManagerInterface $entity_type_manager = NULL) {
parent::__construct($entity_type, $entity_field_manager, $cache, $memory_cache, $entity_type_bundle_info);
$this->database = $database;
$this->languageManager = $language_manager;
if (!$entity_last_installed_schema_repository) {
@trigger_error('Calling SqlContentEntityStorage::__construct() with the $entity_last_installed_schema_repository argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
$entity_last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
}
$this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
if (!$entity_type_manager) {
@trigger_error('Calling SqlContentEntityStorage::__construct() with the $entity_type_manager argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
$entity_type_manager = \Drupal::entityTypeManager();
}
$this->entityTypeManager = $entity_type_manager;
$this->entityType = $this->entityTypeManager->getActiveDefinition($entity_type->id());
$this->fieldStorageDefinitions = $this->entityFieldManager->getActiveFieldStorageDefinitions($entity_type->id());
$this->initTableLayout();
}
......@@ -289,7 +289,7 @@ public function getRevisionDataTable() {
protected function getStorageSchema() {
if (!isset($this->storageSchema)) {
$class = $this->entityType->getHandlerClass('storage_schema') ?: 'Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema';
$this->storageSchema = new $class($this->entityTypeManager, $this->entityType, $this, $this->database, $this->entityFieldManager, $this->entityLastInstalledSchemaRepository);
$this->storageSchema = new $class($this->entityTypeManager, $this->entityType, $this, $this->database, $this->entityFieldManager);
}
return $this->storageSchema;
}
......@@ -313,6 +313,24 @@ public function setEntityType(EntityTypeInterface $entity_type) {
}
}
/**
* Updates the internal list of field storage definitions.
*
* @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions
* An array of field storage definitions.
*
* @internal Only to be used internally by Entity API.
*/
public function setFieldStorageDefinitions(array $field_storage_definitions) {
foreach ($field_storage_definitions as $field_storage_definition) {
if ($field_storage_definition->getTargetEntityTypeId() !== $this->entityType->id()) {
throw new EntityStorageException("Unsupported entity type {$field_storage_definition->getTargetEntityTypeId()}");
}
}
$this->fieldStorageDefinitions = $field_storage_definitions;
}
/**
* Sets the wrapped table mapping definition.
*
......@@ -357,9 +375,7 @@ public function getTableMapping(array $storage_definitions = NULL) {
// If we are using our internal storage definitions, which is our main use
// case, we can statically cache the computed table mapping.
if (!isset($this->tableMapping)) {
$storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
$this->tableMapping = $this->getCustomTableMapping($this->entityType, $storage_definitions);
$this->tableMapping = $this->getCustomTableMapping($this->entityType, $this->fieldStorageDefinitions);
}
return $this->tableMapping;
......@@ -463,7 +479,6 @@ protected function mapFromStorageRecords(array $records, $load_from_revision = F
$field_names = array_unique(array_merge($field_names, $this->tableMapping->getFieldNames($this->revisionTable)));
}
$storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
$values = [];
foreach ($records as $id => $record) {
$values[$id] = [];
......@@ -474,7 +489,7 @@ protected function mapFromStorageRecords(array $records, $load_from_revision = F
$field_columns = $this->tableMapping->getColumnNames($field_name);
// Handle field types that store several properties.
if (count($field_columns) > 1) {
$definition_columns = $storage_definitions[$field_name]->getColumns();
$definition_columns = $this->fieldStorageDefinitions[$field_name]->getColumns();
foreach ($field_columns as $property_name => $column_name) {
if (property_exists($record, $column_name)) {
$values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT][$property_name] = !empty($definition_columns[$property_name]['serialize']) ? unserialize($record->{$column_name}) : $record->{$column_name};
......@@ -486,7 +501,7 @@ protected function mapFromStorageRecords(array $records, $load_from_revision = F
else {
$column_name = reset($field_columns);
if (property_exists($record, $column_name)) {
$columns = $storage_definitions[$field_name]->getColumns();
$columns = $this->fieldStorageDefinitions[$field_name]->getColumns();
$column = reset($columns);
$values[$id][$field_name][LanguageInterface::LANGCODE_DEFAULT] = !empty($column['serialize']) ? unserialize($record->{$column_name}) : $record->{$column_name};
unset($record->{$column_name});
......@@ -582,7 +597,6 @@ protected function loadFromSharedTables(array &$values, array &$translations, $l
$all_fields = $table_mapping->getFieldNames($this->dataTable);
}
$storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
$result = $query->execute();
foreach ($result as $row) {
$id = $row[$record_key];
......@@ -594,7 +608,7 @@ protected function loadFromSharedTables(array &$values, array &$translations, $l
$translations[$id][$langcode] = TRUE;
foreach ($all_fields as $field_name) {
$storage_definition = $storage_definitions[$field_name];
$storage_definition = $this->fieldStorageDefinitions[$field_name];
$definition_columns = $storage_definition->getColumns();
$columns = $table_mapping->getColumnNames($field_name);
// Do not key single-column fields by property name.
......@@ -898,14 +912,13 @@ protected function doSaveFieldItems(ContentEntityInterface $entity, array $names
}
else {
$table_mapping = $this->getTableMapping();
$storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
$shared_table_fields = FALSE;
$dedicated_table_fields = [];
// Collect the name of fields to be written in dedicated tables and check
// whether shared table records need to be updated.
foreach ($names as $name) {
$storage_definition = $storage_definitions[$name];
$storage_definition = $this->fieldStorageDefinitions[$name];
if ($table_mapping->allowsSharedTableStorage($storage_definition)) {
$shared_table_fields = TRUE;
}
......@@ -1059,10 +1072,10 @@ protected function mapToStorageRecord(ContentEntityInterface $entity, $table_nam
$table_mapping = $this->getTableMapping();
foreach ($table_mapping->getFieldNames($table_name) as $field_name) {
if (empty($this->getFieldStorageDefinitions()[$field_name])) {
if (empty($this->fieldStorageDefinitions[$field_name])) {
throw new EntityStorageException("Table mapping contains invalid field $field_name.");
}
$definition = $this->getFieldStorageDefinitions()[$field_name];
$definition = $this->fieldStorageDefinitions[$field_name];
$columns = $table_mapping->getColumnNames($field_name);
foreach ($columns as $column_name => $schema_name) {
......@@ -1420,8 +1433,7 @@ protected function saveToDedicatedTables(ContentEntityInterface $entity, $update
*/
protected function deleteFromDedicatedTables(ContentEntityInterface $entity) {
$table_mapping = $this->getTableMapping();
foreach ($this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) {
$storage_definition = $field_definition->getFieldStorageDefinition();
foreach ($this->fieldStorageDefinitions as $storage_definition) {
if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) {
continue;
}
......@@ -1448,8 +1460,7 @@ protected function deleteRevisionFromDedicatedTables(ContentEntityInterface $ent
$vid = $entity->getRevisionId();
if (isset($vid)) {
$table_mapping = $this->getTableMapping();
foreach ($this->entityFieldManager->getFieldDefinitions($entity->getEntityTypeId(), $entity->bundle()) as $field_definition) {
$storage_definition = $field_definition->getFieldStorageDefinition();
foreach ($this->fieldStorageDefinitions as $storage_definition) {
if (!$table_mapping->requiresDedicatedTableStorage($storage_definition)) {
continue;
}
......@@ -1538,6 +1549,8 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En
public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $storage_definition) {
$this->wrapSchemaException(function () use ($storage_definition) {
$this->getStorageSchema()->onFieldStorageDefinitionCreate($storage_definition);
$this->fieldStorageDefinitions[$storage_definition->getName()] = $storage_definition;
$this->tableMapping = NULL;
});
}
......@@ -1547,6 +1560,8 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $
public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) {
$this->wrapSchemaException(function () use ($storage_definition, $original) {
$this->getStorageSchema()->onFieldStorageDefinitionUpdate($storage_definition, $original);
$this->fieldStorageDefinitions[$storage_definition->getName()] = $storage_definition;
$this->tableMapping = NULL;
});
}
......@@ -1554,10 +1569,7 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $
* {@inheritdoc}
*/
public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $storage_definition) {
$table_mapping = $this->getTableMapping(
$this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($this->entityType->id())
);
$table_mapping = $this->getTableMapping();
if ($table_mapping->requiresDedicatedTableStorage($storage_definition)) {
// Mark all data associated with the field for deletion.
$table = $table_mapping->getDedicatedDataTableName($storage_definition);
......@@ -1575,6 +1587,8 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $
// Update the field schema.
$this->wrapSchemaException(function () use ($storage_definition) {
$this->getStorageSchema()->onFieldStorageDefinitionDelete($storage_definition);
unset($this->fieldStorageDefinitions[$storage_definition->getName()]);
$this->tableMapping = NULL;
});
}
......@@ -1733,7 +1747,7 @@ public function countFieldData($storage_definition, $as_bool = FALSE) {
// storage definition is added, so bypass the internal storage definitions
// and fetch the table mapping using the passed in storage definition.
// @todo Fix this in https://www.drupal.org/node/2705205.
$storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId);
$storage_definitions = $this->fieldStorageDefinitions;
$storage_definitions[$storage_definition->getName()] = $storage_definition;
$table_mapping = $this->getTableMapping($storage_definitions);
......
......@@ -7,7 +7,6 @@
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
use Drupal\Core\Entity\ContentEntityTypeInterface;
use Drupal\Core\Entity\EntityFieldManagerInterface;
use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\EntityPublishedInterface;
use Drupal\Core\Entity\EntityStorageException;
......@@ -54,13 +53,6 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
*/
protected $entityFieldManager;
/**
* The entity last installed schema repository.
*
* @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface
*/
protected $entityLastInstalledSchemaRepository;
/**
* The entity type this schema builder is responsible for.
*
......@@ -123,26 +115,19 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorage
* The database connection to be used.
* @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager
* The entity field manager.
* @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository
* The entity last installed schema repository.
*/
public function __construct(EntityTypeManagerInterface $entity_type_manager, ContentEntityTypeInterface $entity_type, SqlContentEntityStorage $storage, Connection $database, EntityFieldManagerInterface $entity_field_manager = NULL, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository = NULL) {
public function __construct(EntityTypeManagerInterface $entity_type_manager, ContentEntityTypeInterface $entity_type, SqlContentEntityStorage $storage, Connection $database, EntityFieldManagerInterface $entity_field_manager = NULL) {
$this->entityTypeManager = $entity_type_manager;
$this->storage = $storage;
$this->storage = clone $storage;
$this->database = $database;
if (!$entity_field_manager) {
@trigger_error('Calling SqlContentEntityStorageSchema::__construct() with the $entity_field_manager argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
$entity_field_manager = \Drupal::service('entity_field.manager');
}
$this->entityFieldManager = $entity_field_manager;
if (!$entity_last_installed_schema_repository) {
@trigger_error('Calling SqlContentEntityStorageSchema::__construct() with the $entity_last_installed_schema_repository argument is supported in drupal:8.7.0 and will be required before drupal:9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED);
$entity_last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository');
}
$this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository;
$this->entityType = $entity_last_installed_schema_repository->getLastInstalledDefinition($entity_type->id());
$this->fieldStorageDefinitions = $entity_last_installed_schema_repository->getLastInstalledFieldStorageDefinitions($entity_type->id());
$this->entityType = $entity_type_manager->getActiveDefinition($entity_type->id());
$this->fieldStorageDefinitions = $entity_field_manager->getActiveFieldStorageDefinitions($entity_type->id());
}
/**
......@@ -337,9 +322,7 @@ public function requiresEntityDataMigration(EntityTypeInterface $entity_type, En
return FALSE;
}
// Use the original entity type since the storage has not been updated.
$original_storage = $this->entityTypeManager->createHandlerInstance($original_storage_class, $original);
return $original_storage->hasData();
return $this->storage->hasData();
}
/**
......@@ -441,8 +424,6 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) {
* {@inheritdoc}
*/
public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) {
$this->entityType = $entity_type;
$this->fieldStorageDefinitions = $field_storage_definitions;
$this->traitOnFieldableEntityTypeUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox);
}
......@@ -450,47 +431,54 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En
* {@inheritdoc}
*/
protected function preUpdateEntityTypeSchema(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) {
// Make sure that each storage object has a proper table mapping.
/** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $temporary_storage */
$temporary_storage = &$sandbox['temporary_storage'];
$temporary_prefix = static::getTemporaryTableMappingPrefix($entity_type, $field_storage_definitions);
$temporary_table_mapping = $temporary_storage->getCustomTableMapping($entity_type, $field_storage_definitions, $temporary_prefix);
$temporary_storage->setTableMapping($temporary_table_mapping);
/** @var \Drupal\Core\Entity\Sql\SqlContentEntityStorage $original_storage */
$original_storage = &$sandbox['original_storage'];
$original_table_mapping = $original_storage->getCustomTableMapping($original, $original_field_storage_definitions);
$original_storage->setTableMapping($original_table_mapping);
$sandbox['new_table_mapping'] = $temporary_storage->getCustomTableMapping($entity_type, $field_storage_definitions);
$sandbox['backup_table_mapping'] = $original_storage->getCustomTableMapping($original, $original_field_storage_definitions, 'old_');
$sandbox['temporary_table_mapping'] = $this->storage->getCustomTableMapping($entity_type, $field_storage_definitions, $temporary_prefix);
$sandbox['new_table_mapping'] = $this->storage->getCustomTableMapping($entity_type, $field_storage_definitions);
$sandbox['original_table_mapping'] = $this->storage->getCustomTableMapping($original, $original_field_storage_definitions);
$sandbox['backup_table_mapping'] = $this->storage->getCustomTableMapping($original, $original_field_storage_definitions, 'old_');
// Create temporary tables based on the new entity type and field storage
// definitions.
$schema_handler = $this->database->schema();
$temporary_table_names = array_combine($sandbox['new_table_mapping']->getTableNames(), $sandbox['temporary_table_mapping']->getTableNames());
$this->entityType = $entity_type;
$this->fieldStorageDefinitions = $field_storage_definitions;
// Update the storage's entity type and field storage definitions because
// ::getEntitySchema() and ::getSharedTableFieldSchema() overrides are
// retrieving table names from these definitions.
$this->storage->setEntityType($entity_type);
$this->storage->setFieldStorageDefinitions($field_storage_definitions);
$this->storage->setTableMapping($sandbox['new_table_mapping']);
// Create entity tables.
$temporary_table_names = array_combine($sandbox['new_table_mapping']->getTableNames(), $temporary_table_mapping->getTableNames());
$schema = $this->getEntitySchema($entity_type, TRUE);
$sandbox['new_entity_schema'] = $schema;
// Filter out tables which are not part of the table mapping.
$schema = array_intersect_key($schema, $temporary_table_names);
// Create entity tables.
foreach ($schema as $table_name => $table_schema) {
$schema_handler->createTable($temporary_table_names[$table_name], $table_schema);