diff --git a/core/core.services.yml b/core/core.services.yml index 20e5da30d8..08b09d9953 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -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 } diff --git a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php index 0040ae076a..3ec93063ad 100644 --- a/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php +++ b/core/lib/Drupal/Core/Entity/ContentEntityStorageBase.php @@ -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) { diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php index 5baf85fd1f..58aede1920 100644 --- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php +++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php @@ -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(); } /** diff --git a/core/lib/Drupal/Core/Entity/EntityFieldManager.php b/core/lib/Drupal/Core/Entity/EntityFieldManager.php index cad945cddf..0357e99755 100644 --- a/core/lib/Drupal/Core/Entity/EntityFieldManager.php +++ b/core/lib/Drupal/Core/Entity/EntityFieldManager.php @@ -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,16 @@ public function getFieldStorageDefinitions($entity_type_id) { return $this->fieldStorageDefinitions[$entity_type_id]; } + /** + * {@inheritdoc} + */ + 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 +586,7 @@ public function clearCachedFieldDefinitions() { $this->baseFieldDefinitions = []; $this->fieldDefinitions = []; $this->fieldStorageDefinitions = []; + $this->activeFieldStorageDefinitions = []; $this->fieldMap = []; $this->fieldMapByFieldType = []; $this->entityDisplayRepository->clearDisplayModeInfo(); @@ -588,6 +606,7 @@ public function useCaches($use_caches = FALSE) { $this->fieldDefinitions = []; $this->baseFieldDefinitions = []; $this->fieldStorageDefinitions = []; + $this->activeFieldStorageDefinitions = []; } } diff --git a/core/lib/Drupal/Core/Entity/EntityFieldManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityFieldManagerInterface.php index 327d9e309f..016fe15782 100644 --- a/core/lib/Drupal/Core/Entity/EntityFieldManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityFieldManagerInterface.php @@ -61,6 +61,18 @@ public function getFieldDefinitions($entity_type_id, $bundle); */ public function getFieldStorageDefinitions($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. + */ + public function getActiveFieldStorageDefinitions($entity_type_id); + /** * Gets a lightweight map of fields across bundles. * diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php index 91a904da5e..475e051fa0 100644 --- a/core/lib/Drupal/Core/Entity/EntityManager.php +++ b/core/lib/Drupal/Core/Entity/EntityManager.php @@ -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} * diff --git a/core/lib/Drupal/Core/Entity/EntityTypeManager.php b/core/lib/Drupal/Core/Entity/EntityTypeManager.php index 9fecdb542a..fe809af41e 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeManager.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeManager.php @@ -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,23 @@ 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)); } + /** + * {@inheritdoc} + */ + 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 +176,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(); } diff --git a/core/lib/Drupal/Core/Entity/EntityTypeManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeManagerInterface.php index 4a5ec77745..5ef06443bf 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeManagerInterface.php @@ -148,4 +148,15 @@ public function getDefinition($entity_type_id, $exception_on_invalid = TRUE); */ public function getDefinitions(); + /** + * 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. + */ + public function getActiveDefinition($entity_type_id); + } diff --git a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php index f550b62a80..7257caa7ab 100644 --- a/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php +++ b/core/lib/Drupal/Core/Entity/Query/Sql/Tables.php @@ -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); } diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index 2f770aede1..5e3fa64289 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -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. * @@ -165,8 +164,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 +195,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 +290,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 +314,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 +376,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 +480,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 +490,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 +502,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 +598,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 +609,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 +913,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 +1073,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 +1434,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 +1461,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; } @@ -1554,10 +1566,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); @@ -1733,7 +1742,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); diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index 1459f01c05..f6eb340f9e 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -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,10 +115,8 @@ 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->database = $database; @@ -135,14 +125,9 @@ public function __construct(EntityTypeManagerInterface $entity_type_manager, Con $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); + $this->database->schema()->createTable($temporary_table_names[$table_name], $table_schema); } // Create dedicated field tables. foreach ($field_storage_definitions as $field_storage_definition) { - if ($temporary_table_mapping->requiresDedicatedTableStorage($field_storage_definition)) { + if ($sandbox['temporary_table_mapping']->requiresDedicatedTableStorage($field_storage_definition)) { $schema = $this->getDedicatedTableSchema($field_storage_definition, $entity_type); // Filter out tables which are not part of the table mapping. $schema = array_intersect_key($schema, $temporary_table_names); foreach ($schema as $table_name => $table_schema) { - $schema_handler->createTable($temporary_table_names[$table_name], $table_schema); + $this->database->schema()->createTable($temporary_table_names[$table_name], $table_schema); } } } + + // Restore the original definitions and table mapping so the data copying + // step can load existing data properly. + $this->storage->setEntityType($original); + $this->storage->setFieldStorageDefinitions($original_field_storage_definitions); + $this->storage->setTableMapping($sandbox['original_table_mapping']); } /** @@ -498,9 +486,9 @@ protected function preUpdateEntityTypeSchema(EntityTypeInterface $entity_type, E */ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) { /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $temporary_table_mapping */ - $temporary_table_mapping = $sandbox['temporary_storage']->getTableMapping(); + $temporary_table_mapping = $sandbox['temporary_table_mapping']; /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $original_table_mapping */ - $original_table_mapping = $sandbox['original_storage']->getTableMapping(); + $original_table_mapping = $sandbox['original_table_mapping']; /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $new_table_mapping */ $new_table_mapping = $sandbox['new_table_mapping']; /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $backup_table_mapping */ @@ -534,8 +522,11 @@ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type, } // Store the updated entity schema. - $entity_schema = $this->getEntitySchema($entity_type, TRUE); - $this->saveEntitySchemaData($entity_type, $entity_schema); + $new_entity_schema = $sandbox['new_entity_schema']; + $this->schema[$entity_type->id()] = $new_entity_schema; + $this->entityType = $entity_type; + $this->fieldStorageDefinitions = $field_storage_definitions; + $this->saveEntitySchemaData($entity_type, $new_entity_schema); // Store the updated field schema for each field storage. foreach ($field_storage_definitions as $field_storage_definition) { @@ -581,7 +572,7 @@ protected function handleEntityTypeSchemaUpdateExceptionOnDataCopy(EntityTypeInt // In case of an error during the save process, we need to clean up the // temporary tables. /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $temporary_table_mapping */ - $temporary_table_mapping = $sandbox['temporary_storage']->getTableMapping(); + $temporary_table_mapping = $sandbox['temporary_table_mapping']; foreach ($temporary_table_mapping->getTableNames() as $table_name) { $this->database->schema()->dropTable($table_name); } diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php b/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php index 4b31a185c5..25f1ac0f9a 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlFieldableEntityTypeListenerTrait.php @@ -58,15 +58,12 @@ public function onFieldableEntityTypeUpdate(EntityTypeInterface $entity_type, En throw new EntityStorageException('Missing revision_translation_affected field.'); } - $sandbox['original_storage'] = $original_storage; - $sandbox['temporary_storage'] = $this->entityTypeManager->createHandlerInstance($entity_type->getStorageClass(), $entity_type); - $this->preUpdateEntityTypeSchema($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } // Copy data from the original storage to the temporary one. if ($has_data) { - $this->copyData($entity_type, $original, $sandbox); + $this->copyData($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } else { // If there is no existing data, we still need to run the @@ -127,10 +124,14 @@ protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type, * The updated entity type definition. * @param \Drupal\Core\Entity\EntityTypeInterface $original * The original entity type definition. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions + * The updated field storage definitions, including possibly new ones. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $original_field_storage_definitions + * The original field storage definitions. * @param array &$sandbox * The sandbox array from a hook_update_N() implementation. */ - protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterface $original, array &$sandbox) { + protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox) { /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ $id_key = $entity_type->getKey('id'); $revision_id_key = $entity_type->getKey('revision'); @@ -139,9 +140,6 @@ protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterfac $default_langcode_key = $entity_type->getKey('default_langcode'); $revision_translation_affected_key = $entity_type->getKey('revision_translation_affected'); - $temporary_storage = $sandbox['temporary_storage']; - $original_storage = $sandbox['original_storage']; - // If 'progress' is not set, then this will be the first run of the batch. if (!isset($sandbox['progress'])) { $sandbox['progress'] = 0; @@ -174,7 +172,13 @@ protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterfac ->fetchCol(); /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ - $entities = $load_revisions ? $original_storage->loadMultipleRevisions($entity_identifiers) : $original_storage->loadMultiple($entity_identifiers); + $entities = $load_revisions ? $this->storage->loadMultipleRevisions($entity_identifiers) : $this->storage->loadMultiple($entity_identifiers); + + $temporary_storage = $this->entityTypeManager->createHandlerInstance($entity_type->getStorageClass(), $entity_type); + $temporary_storage->setEntityType($entity_type); + $temporary_storage->setFieldStorageDefinitions($field_storage_definitions); + $temporary_storage->setTableMapping($sandbox['temporary_table_mapping']); + foreach ($entities as $identifier => $entity) { try { if (!$original->isRevisionable() && $entity_type->isRevisionable()) { diff --git a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php index e67629b759..81778d9692 100644 --- a/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php +++ b/core/lib/Drupal/Core/Field/FieldStorageDefinitionListener.php @@ -2,12 +2,10 @@ namespace Drupal\Core\Field; -use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\FieldableEntityStorageInterface; -use Drupal\Core\Entity\Sql\SqlContentEntityStorage; use Symfony\Component\EventDispatcher\EventDispatcherInterface; /** @@ -82,17 +80,7 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $ // @todo Forward this to all interested handlers, not only storage, once // iterating handlers is possible: https://www.drupal.org/node/2332857. - $storage = clone $this->entityTypeManager->getStorage($entity_type_id); - - // Entity type definition updates can change the schema by adding or - // removing entity tables (for example when switching an entity type from - // non-revisionable to revisionable), so CRUD operations on a field storage - // definition need to use the last installed entity type schema. - if ($storage instanceof SqlContentEntityStorage - && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) { - $storage->setEntityType($last_installed_entity_type); - } - + $storage = $this->entityTypeManager->getStorage($entity_type_id); if ($storage instanceof FieldStorageDefinitionListenerInterface) { $storage->onFieldStorageDefinitionCreate($storage_definition); } @@ -100,6 +88,7 @@ public function onFieldStorageDefinitionCreate(FieldStorageDefinitionInterface $ $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::CREATE, new FieldStorageDefinitionEvent($storage_definition)); $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinition($storage_definition); + $this->entityTypeManager->clearCachedDefinitions(); $this->entityFieldManager->clearCachedFieldDefinitions(); } @@ -111,17 +100,7 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $ // @todo Forward this to all interested handlers, not only storage, once // iterating handlers is possible: https://www.drupal.org/node/2332857. - $storage = clone $this->entityTypeManager->getStorage($entity_type_id); - - // Entity type definition updates can change the schema by adding or - // removing entity tables (for example when switching an entity type from - // non-revisionable to revisionable), so CRUD operations on a field storage - // definition need to use the last installed entity type schema. - if ($storage instanceof SqlContentEntityStorage - && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) { - $storage->setEntityType($last_installed_entity_type); - } - + $storage = $this->entityTypeManager->getStorage($entity_type_id); if ($storage instanceof FieldStorageDefinitionListenerInterface) { $storage->onFieldStorageDefinitionUpdate($storage_definition, $original); } @@ -129,6 +108,7 @@ public function onFieldStorageDefinitionUpdate(FieldStorageDefinitionInterface $ $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::UPDATE, new FieldStorageDefinitionEvent($storage_definition, $original)); $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinition($storage_definition); + $this->entityTypeManager->clearCachedDefinitions(); $this->entityFieldManager->clearCachedFieldDefinitions(); } @@ -140,32 +120,15 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $ // @todo Forward this to all interested handlers, not only storage, once // iterating handlers is possible: https://www.drupal.org/node/2332857. - $storage = clone $this->entityTypeManager->getStorage($entity_type_id); - - // Entity type definition updates can change the schema by adding or - // removing entity tables (for example when switching an entity type from - // non-revisionable to revisionable), so CRUD operations on a field storage - // definition need to use the last installed entity type schema. - if ($storage instanceof SqlContentEntityStorage - && ($last_installed_entity_type = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id))) { - $storage->setEntityType($last_installed_entity_type); - } + $storage = $this->entityTypeManager->getStorage($entity_type_id); // Keep the field definition in the deleted fields repository so we can use // it later during field_purge_batch(), but only if the field has data. - try { - if ($storage_definition instanceof BaseFieldDefinition && $storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) { - $deleted_storage_definition = clone $storage_definition; - $deleted_storage_definition->setDeleted(TRUE); - $this->deletedFieldsRepository->addFieldDefinition($deleted_storage_definition); - $this->deletedFieldsRepository->addFieldStorageDefinition($deleted_storage_definition); - } - } - catch (DatabaseExceptionWrapper $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. + if ($storage_definition instanceof BaseFieldDefinition && $storage instanceof FieldableEntityStorageInterface && $storage->countFieldData($storage_definition, TRUE)) { + $deleted_storage_definition = clone $storage_definition; + $deleted_storage_definition->setDeleted(TRUE); + $this->deletedFieldsRepository->addFieldDefinition($deleted_storage_definition); + $this->deletedFieldsRepository->addFieldStorageDefinition($deleted_storage_definition); } if ($storage instanceof FieldStorageDefinitionListenerInterface) { @@ -175,6 +138,7 @@ public function onFieldStorageDefinitionDelete(FieldStorageDefinitionInterface $ $this->eventDispatcher->dispatch(FieldStorageDefinitionEvents::DELETE, new FieldStorageDefinitionEvent($storage_definition)); $this->entityLastInstalledSchemaRepository->deleteLastInstalledFieldStorageDefinition($storage_definition); + $this->entityTypeManager->clearCachedDefinitions(); $this->entityFieldManager->clearCachedFieldDefinitions(); } diff --git a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php index eaa4ad64d9..5311d88f5c 100644 --- a/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php +++ b/core/modules/content_moderation/tests/src/Kernel/ContentModerationStateTest.php @@ -531,7 +531,7 @@ public function testNonLangcodeEntityTypeModeration() { \Drupal::state()->set('entity_test_rev.entity_type', $entity_type); // Update the entity type in order to remove the 'langcode' field. - $this->applyEntityUpdates('entity_test_rev'); + \Drupal::entityDefinitionUpdateManager()->updateFieldableEntityType($entity_type, \Drupal::service('entity_field.manager')->getFieldStorageDefinitions($entity_type->id())); $workflow = $this->createEditorialWorkflow(); $workflow->getTypePlugin()->addEntityTypeAndBundle('entity_test_rev', 'entity_test_rev'); diff --git a/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php b/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php index a3d9c4d942..15ed5791ea 100644 --- a/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php +++ b/core/modules/node/tests/src/Functional/NodeAccessLanguageTest.php @@ -96,7 +96,7 @@ public function testNodeAccess() { $this->assertNodeAccess($expected_node_access_no_access, $node_public_ca, $web_user); $this->assertNodeAccess($expected_node_access_no_access, $node_public_ca->getTranslation('ca'), $web_user); - \Drupal::entityManager()->getAccessControlHandler('node')->resetCache(); + $this->accessHandler->resetCache(); // Tests that access is granted if requested with no language. $this->assertNodeAccess($expected_node_access, $node_public_no_language, $web_user); @@ -141,7 +141,7 @@ public function testNodeAccessPrivate() { $this->assertNodeAccess($expected_node_access_no_access, $node_private_no_language, $web_user); // Reset the node access cache and turn on our test node access code. - \Drupal::entityManager()->getAccessControlHandler('node')->resetCache(); + $this->accessHandler->resetCache(); \Drupal::state()->set('node_access_test_secret_catalan', 1); // Tests that access is not granted if requested with no language. @@ -159,7 +159,7 @@ public function testNodeAccessPrivate() { $this->assertNodeAccess($expected_node_access_no_access, $node_private_ca, $private_ca_user); $this->assertNodeAccess($expected_node_access_no_access, $node_private_ca->getTranslation('ca'), $private_ca_user); - \Drupal::entityManager()->getAccessControlHandler('node')->resetCache(); + $this->accessHandler->resetCache(); \Drupal::state()->set('node_access_test_secret_catalan', 0); // Tests that Catalan is still not accessible for a user with no access to diff --git a/core/modules/workspaces/src/EntityQuery/Tables.php b/core/modules/workspaces/src/EntityQuery/Tables.php index 9ae727b5e7..b791b92e2e 100644 --- a/core/modules/workspaces/src/EntityQuery/Tables.php +++ b/core/modules/workspaces/src/EntityQuery/Tables.php @@ -97,7 +97,7 @@ protected function addJoin($type, $table, $join_condition, $langcode, $delta = N if (isset($this->baseTablesEntityType[$base_table])) { $entity_type_id = $this->baseTablesEntityType[$base_table]; - $revision_key = $this->entityManager->getDefinition($entity_type_id)->getKey('revision'); + $revision_key = $this->entityTypeManager->getActiveDefinition($entity_type_id)->getKey('revision'); if ($id_field === $revision_key || $id_field === 'revision_id') { $workspace_association_table = $this->contentWorkspaceTables[$base_table]; @@ -141,7 +141,7 @@ protected function addNextBaseTable(EntityType $entity_type, $table, $sql_column */ public function addWorkspaceAssociationJoin($entity_type_id, $base_table_alias, $active_workspace_id) { if (!isset($this->contentWorkspaceTables[$base_table_alias])) { - $entity_type = $this->entityManager->getDefinition($entity_type_id); + $entity_type = $this->entityTypeManager->getActiveDefinition($entity_type_id); $id_field = $entity_type->getKey('id'); // LEFT join the Workspace association entity's table so we can properly diff --git a/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php index bff60a78f9..d3beb19f73 100644 --- a/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php +++ b/core/tests/Drupal/KernelTests/Core/Entity/FieldableEntityDefinitionUpdateTest.php @@ -144,6 +144,12 @@ public function testFieldableEntityTypeUpdates($initial_rev, $initial_mul, $new_ $this->setExpectedException(EntityStorageException::class, 'Converting an entity type from revisionable to non-revisionable or from translatable to non-translatable is not supported.'); } + // Check that existing data can be retrieved from the storage before the + // entity schema is updated. + if ($data_migration_supported) { + $this->assertEntityData($initial_rev, $initial_mul); + } + // Simulate a batch run since we are converting the entities one by one. $sandbox = []; do { @@ -153,6 +159,9 @@ public function testFieldableEntityTypeUpdates($initial_rev, $initial_mul, $new_ $this->assertEntityTypeSchema($new_rev, $new_mul); $this->assertEntityData($initial_rev, $initial_mul); + $change_list = $this->entityDefinitionUpdateManager->getChangeList(); + $this->assertArrayNotHasKey('entity_test_update', $change_list, "There are no remaining updates for the 'entity_test_update' entity type."); + // Check that we can still save new entities after the schema has been // updated. $this->insertData($new_rev, $new_mul); @@ -283,8 +292,14 @@ protected function insertData($revisionable, $translatable) { 'value2' => 'shared table - ' . $i . ' - value 2 - en', ], 'test_multiple_properties_multiple_values' => [ - 'value1' => 'dedicated table - ' . $i . ' - value 1 - en', - 'value2' => 'dedicated table - ' . $i . ' - value 2 - en', + [ + 'value1' => 'dedicated table - ' . $i . ' - delta 0 - value 1 - en', + 'value2' => 'dedicated table - ' . $i . ' - delta 0 - value 2 - en', + ], + [ + 'value1' => 'dedicated table - ' . $i . ' - delta 1 - value 1 - en', + 'value2' => 'dedicated table - ' . $i . ' - delta 1 - value 2 - en', + ], ], ]); $entity->save(); @@ -297,8 +312,14 @@ protected function insertData($revisionable, $translatable) { 'value2' => 'shared table - ' . $i . ' - value 2 - ro', ], 'test_multiple_properties_multiple_values' => [ - 'value1' => 'dedicated table - ' . $i . ' - value 1 - ro', - 'value2' => 'dedicated table - ' . $i . ' - value 2 - ro', + [ + 'value1' => 'dedicated table - ' . $i . ' - delta 0 - value 1 - ro', + 'value2' => 'dedicated table - ' . $i . ' - delta 0 - value 2 - ro', + ], + [ + 'value1' => 'dedicated table - ' . $i . ' - delta 1 - value 1 - ro', + 'value2' => 'dedicated table - ' . $i . ' - delta 1 - value 2 - ro', + ], ], ]); $translation->save(); @@ -309,15 +330,23 @@ protected function insertData($revisionable, $translatable) { // Create a new pending revision. $revision_2 = $storage->createRevision($entity, FALSE); $revision_2->name = 'test entity - ' . $i . ' - en - rev2'; - $revision_2->test_multiple_properties_multiple_values->value1 = 'dedicated table - ' . $i . ' - value 1 - en - rev2'; - $revision_2->test_multiple_properties_multiple_values->value2 = 'dedicated table - ' . $i . ' - value 2 - en - rev2'; + $revision_2->test_multiple_properties->value1 = 'shared table - ' . $i . ' - value 1 - en - rev2'; + $revision_2->test_multiple_properties->value2 = 'shared table - ' . $i . ' - value 2 - en - rev2'; + $revision_2->test_multiple_properties_multiple_values[0]->value1 = 'dedicated table - ' . $i . ' - delta 0 - value 1 - en - rev2'; + $revision_2->test_multiple_properties_multiple_values[0]->value2 = 'dedicated table - ' . $i . ' - delta 0 - value 2 - en - rev2'; + $revision_2->test_multiple_properties_multiple_values[1]->value1 = 'dedicated table - ' . $i . ' - delta 1 - value 1 - en - rev2'; + $revision_2->test_multiple_properties_multiple_values[1]->value2 = 'dedicated table - ' . $i . ' - delta 1 - value 2 - en - rev2'; $revision_2->save(); if ($translatable) { $revision_2_translation = $storage->createRevision($entity->getTranslation('ro'), FALSE); $revision_2_translation->name = 'test entity - ' . $i . ' - ro - rev2'; - $revision_2_translation->test_multiple_properties_multiple_values->value1 = 'dedicated table - ' . $i . ' - value 1 - ro - rev2'; - $revision_2_translation->test_multiple_properties_multiple_values->value2 = 'dedicated table - ' . $i . ' - value 2 - ro - rev2'; + $revision_2->test_multiple_properties->value1 = 'shared table - ' . $i . ' - value 1 - ro - rev2'; + $revision_2->test_multiple_properties->value2 = 'shared table - ' . $i . ' - value 2 - ro - rev2'; + $revision_2_translation->test_multiple_properties_multiple_values[0]->value1 = 'dedicated table - ' . $i . ' - delta 0 - value 1 - ro - rev2'; + $revision_2_translation->test_multiple_properties_multiple_values[0]->value2 = 'dedicated table - ' . $i . ' - delta 0 - value 2 - ro - rev2'; + $revision_2_translation->test_multiple_properties_multiple_values[1]->value1 = 'dedicated table - ' . $i . ' - delta 1 - value 1 - ro - rev2'; + $revision_2_translation->test_multiple_properties_multiple_values[1]->value2 = 'dedicated table - ' . $i . ' - delta 1 - value 2 - ro - rev2'; $revision_2_translation->save(); } } @@ -338,14 +367,22 @@ protected function assertEntityData($revisionable, $translatable) { foreach ($entities as $entity_id => $entity) { /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ $this->assertEquals("test entity - {$entity->id()} - en", $entity->label()); - $this->assertEquals("dedicated table - {$entity->id()} - value 1 - en", $entity->test_multiple_properties_multiple_values->value1); - $this->assertEquals("dedicated table - {$entity->id()} - value 2 - en", $entity->test_multiple_properties_multiple_values->value2); + $this->assertEquals("shared table - {$entity->id()} - value 1 - en", $entity->test_multiple_properties->value1); + $this->assertEquals("shared table - {$entity->id()} - value 2 - en", $entity->test_multiple_properties->value2); + $this->assertEquals("dedicated table - {$entity->id()} - delta 0 - value 1 - en", $entity->test_multiple_properties_multiple_values[0]->value1); + $this->assertEquals("dedicated table - {$entity->id()} - delta 0 - value 2 - en", $entity->test_multiple_properties_multiple_values[0]->value2); + $this->assertEquals("dedicated table - {$entity->id()} - delta 1 - value 1 - en", $entity->test_multiple_properties_multiple_values[1]->value1); + $this->assertEquals("dedicated table - {$entity->id()} - delta 1 - value 2 - en", $entity->test_multiple_properties_multiple_values[1]->value2); if ($translatable) { $translation = $entity->getTranslation('ro'); - $this->assertEquals("test entity - {$entity->id()} - ro", $translation->label()); - $this->assertEquals("dedicated table - {$translation->id()} - value 1 - ro", $translation->test_multiple_properties_multiple_values->value1); - $this->assertEquals("dedicated table - {$translation->id()} - value 2 - ro", $translation->test_multiple_properties_multiple_values->value2); + $this->assertEquals("test entity - {$translation->id()} - ro", $translation->label()); + $this->assertEquals("shared table - {$translation->id()} - value 1 - ro", $translation->test_multiple_properties->value1); + $this->assertEquals("shared table - {$translation->id()} - value 2 - ro", $translation->test_multiple_properties->value2); + $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 1 - ro", $translation->test_multiple_properties_multiple_values[0]->value1); + $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 2 - ro", $translation->test_multiple_properties_multiple_values[0]->value2); + $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 1 - ro", $translation->test_multiple_properties_multiple_values[1]->value1); + $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 2 - ro", $translation->test_multiple_properties_multiple_values[1]->value2); } } @@ -358,14 +395,22 @@ protected function assertEntityData($revisionable, $translatable) { /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ $revision_label = $revision->isDefaultRevision() ? NULL : ' - rev2'; $this->assertEquals("test entity - {$revision->id()} - en{$revision_label}", $revision->label()); - $this->assertEquals("dedicated table - {$revision->id()} - value 1 - en{$revision_label}", $revision->test_multiple_properties_multiple_values->value1); - $this->assertEquals("dedicated table - {$revision->id()} - value 2 - en{$revision_label}", $revision->test_multiple_properties_multiple_values->value2); + $this->assertEquals("shared table - {$revision->id()} - value 1 - en{$revision_label}", $revision->test_multiple_properties->value1); + $this->assertEquals("shared table - {$revision->id()} - value 2 - en{$revision_label}", $revision->test_multiple_properties->value2); + $this->assertEquals("dedicated table - {$revision->id()} - delta 0 - value 1 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[0]->value1); + $this->assertEquals("dedicated table - {$revision->id()} - delta 0 - value 2 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[0]->value2); + $this->assertEquals("dedicated table - {$revision->id()} - delta 1 - value 1 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[1]->value1); + $this->assertEquals("dedicated table - {$revision->id()} - delta 1 - value 2 - en{$revision_label}", $revision->test_multiple_properties_multiple_values[1]->value2); if ($translatable) { $translation = $revision->getTranslation('ro'); $this->assertEquals("test entity - {$translation->id()} - ro{$revision_label}", $translation->label()); - $this->assertEquals("dedicated table - {$translation->id()} - value 1 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values->value1); - $this->assertEquals("dedicated table - {$translation->id()} - value 2 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values->value2); + $this->assertEquals("shared table - {$revision->id()} - value 1 - ro{$revision_label}", $translation->test_multiple_properties->value1); + $this->assertEquals("shared table - {$revision->id()} - value 2 - ro{$revision_label}", $translation->test_multiple_properties->value2); + $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 1 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[0]->value1); + $this->assertEquals("dedicated table - {$translation->id()} - delta 0 - value 2 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[0]->value2); + $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 1 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[1]->value1); + $this->assertEquals("dedicated table - {$translation->id()} - delta 1 - value 2 - ro{$revision_label}", $translation->test_multiple_properties_multiple_values[1]->value2); } } } @@ -659,18 +704,22 @@ public function testFieldableEntityTypeUpdatesErrorHandling() { ->condition('entity_id', 1, '=') ->condition('langcode', 'en', '=') ->execute() - ->fetchAllAssoc('entity_id'); - $this->assertEquals('dedicated table - 1 - value 1 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1); - $this->assertEquals('dedicated table - 1 - value 2 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2); + ->fetchAllAssoc('delta'); + $this->assertEquals('dedicated table - 1 - delta 0 - value 1 - en', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value1); + $this->assertEquals('dedicated table - 1 - delta 0 - value 2 - en', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value2); + $this->assertEquals('dedicated table - 1 - delta 1 - value 1 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1); + $this->assertEquals('dedicated table - 1 - delta 1 - value 2 - en', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2); $dedicated_table_row = $this->database->select($dedicated_table_name) ->fields($dedicated_table_name) ->condition('entity_id', 1, '=') ->condition('langcode', 'ro', '=') ->execute() - ->fetchAllAssoc('entity_id'); - $this->assertEquals('dedicated table - 1 - value 1 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1); - $this->assertEquals('dedicated table - 1 - value 2 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2); + ->fetchAllAssoc('delta'); + $this->assertEquals('dedicated table - 1 - delta 0 - value 1 - ro', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value1); + $this->assertEquals('dedicated table - 1 - delta 0 - value 2 - ro', $dedicated_table_row[0]->test_multiple_properties_multiple_values_value2); + $this->assertEquals('dedicated table - 1 - delta 1 - value 1 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value1); + $this->assertEquals('dedicated table - 1 - delta 1 - value 2 - ro', $dedicated_table_row[1]->test_multiple_properties_multiple_values_value2); } } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php index 0b1a3329af..3efc859b15 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityManagerTest.php @@ -452,4 +452,22 @@ public function testGetCanonicalMultiple() { $this->entityManager->getCanonicalMultiple($entity_type_id, $entity_ids, $contexts); } + /** + * @covers ::getActiveDefinition + * + * @expectedDeprecation 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. + */ + public function testGetActiveDefinition() { + $this->entityManager->getActiveDefinition('entity_test'); + } + + /** + * @covers ::getActiveFieldStorageDefinitions + * + * @expectedDeprecation 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. + */ + public function testGetActiveFieldStorageDefinitions() { + $this->entityManager->getActiveFieldStorageDefinitions('entity_test'); + } + } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php index 4c2d0893d4..70cac60011 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityTypeManagerTest.php @@ -14,6 +14,7 @@ use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityHandlerBase; use Drupal\Core\Entity\EntityInterface; +use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface; use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeManager; @@ -66,6 +67,13 @@ class EntityTypeManagerTest extends UnitTestCase { */ protected $cacheBackend; + /** + * The entity last installed schema repository. + * + * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface|\Prophecy\Prophecy\ProphecyInterface + */ + protected $entityLastInstalledSchemaRepository; + /** * {@inheritdoc} */ @@ -78,8 +86,9 @@ protected function setUp() { $this->cacheBackend = $this->prophesize(CacheBackendInterface::class); $this->translationManager = $this->prophesize(TranslationInterface::class); + $this->entityLastInstalledSchemaRepository = $this->prophesize(EntityLastInstalledSchemaRepositoryInterface::class); - $this->entityTypeManager = new TestEntityTypeManager(new \ArrayObject(), $this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->translationManager->reveal(), $this->getClassResolverStub()); + $this->entityTypeManager = new TestEntityTypeManager(new \ArrayObject(), $this->moduleHandler->reveal(), $this->cacheBackend->reveal(), $this->translationManager->reveal(), $this->getClassResolverStub(), $this->entityLastInstalledSchemaRepository->reveal()); $this->discovery = $this->prophesize(DiscoveryInterface::class); $this->entityTypeManager->setDiscovery($this->discovery->reveal()); } diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php index f3dec947b8..d624f84b51 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php @@ -1203,26 +1203,14 @@ public function testRequiresEntityDataMigration($updated_entity_type_definition, 'entity_keys' => ['id' => 'id'], ]); - $original_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->disableOriginalConstructor() - ->getMock(); - - $original_storage->expects($this->exactly(is_null($original_storage_has_data) || !$shared_table_structure_changed ? 0 : 1)) + $this->storage->expects($this->exactly(is_null($original_storage_has_data) || !$shared_table_structure_changed ? 0 : 1)) ->method('hasData') ->willReturn($original_storage_has_data); - // Assert hasData() is never called on the new storage definition. - $this->storage->expects($this->never()) - ->method('hasData'); - $connection = $this->getMockBuilder('Drupal\Core\Database\Connection') ->disableOriginalConstructor() ->getMock(); - $this->entityTypeManager->expects($this->any()) - ->method('createHandlerInstance') - ->willReturn($original_storage); - $this->entityLastInstalledSchemaRepository ->expects($this->any()) ->method('getLastInstalledDefinition') @@ -1372,11 +1360,21 @@ protected function setUpStorageSchema(array $expected = []) { ->with($this->entityType->id()) ->will($this->returnValue($this->entityType)); + $this->entityTypeManager->expects($this->any()) + ->method('getActiveDefinition') + ->with($this->entityType->id()) + ->will($this->returnValue($this->entityType)); + $this->entityFieldManager->expects($this->any()) ->method('getFieldStorageDefinitions') ->with($this->entityType->id()) ->will($this->returnValue($this->storageDefinitions)); + $this->entityFieldManager->expects($this->any()) + ->method('getActiveFieldStorageDefinitions') + ->with($this->entityType->id()) + ->will($this->returnValue($this->storageDefinitions)); + $this->dbSchemaHandler = $this->getMockBuilder('Drupal\Core\Database\Schema') ->disableOriginalConstructor() ->getMock(); diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php index 4ff4e3ad6b..1f39be1a7c 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageTest.php @@ -12,7 +12,6 @@ use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\Core\Entity\EntityFieldManagerInterface; use Drupal\Core\Entity\EntityInterface; -use Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface; use Drupal\Core\Entity\EntityStorageInterface; use Drupal\Core\Entity\EntityTypeBundleInfoInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; @@ -70,13 +69,6 @@ class SqlContentEntityStorageTest extends UnitTestCase { */ protected $entityFieldManager; - /** - * The mocked entity last installed schema repository used in this test. - * - * @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject - */ - protected $entityLastInstalledSchemaRepository; - /** * The entity type ID. * @@ -134,7 +126,6 @@ protected function setUp() { $this->entityTypeManager = $this->getMock(EntityTypeManagerInterface::class); $this->entityTypeBundleInfo = $this->createMock(EntityTypeBundleInfoInterface::class); $this->entityFieldManager = $this->getMock(EntityFieldManagerInterface::class); - $this->entityLastInstalledSchemaRepository = $this->createMock(EntityLastInstalledSchemaRepositoryInterface::class); $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface'); $this->cache = $this->getMock('Drupal\Core\Cache\CacheBackendInterface'); $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); @@ -147,7 +138,6 @@ protected function setUp() { $this->container->set('entity_type.manager', $this->entityTypeManager); $this->container->set('entity_field.manager', $this->entityFieldManager); - $this->container->set('entity.last_installed_schema.repository', $this->entityLastInstalledSchemaRepository); } /** @@ -439,13 +429,13 @@ public function testOnEntityTypeCreate() { ->will($this->returnValue($schema_handler)); $storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository]) + ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager]) ->setMethods(['getStorageSchema']) ->getMock(); $key_value = $this->getMock('Drupal\Core\KeyValueStore\KeyValueStoreInterface'); $schema_handler = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorageSchema') - ->setConstructorArgs([$this->entityTypeManager, $this->entityType, $storage, $this->connection, $this->entityFieldManager, $this->entityLastInstalledSchemaRepository]) + ->setConstructorArgs([$this->entityTypeManager, $this->entityType, $storage, $this->connection, $this->entityFieldManager]) ->setMethods(['installedStorageSchema', 'createSharedTableSchema']) ->getMock(); $schema_handler @@ -1175,15 +1165,19 @@ protected function setUpEntityStorage() { ->method('getDefinition') ->will($this->returnValue($this->entityType)); + $this->entityTypeManager->expects($this->any()) + ->method('getActiveDefinition') + ->will($this->returnValue($this->entityType)); + $this->entityFieldManager->expects($this->any()) ->method('getFieldStorageDefinitions') ->will($this->returnValue($this->fieldDefinitions)); $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') + ->method('getActiveFieldStorageDefinitions') ->will($this->returnValue($this->fieldDefinitions)); - $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository); + $this->entityStorage = new SqlContentEntityStorage($this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager); $this->entityStorage->setModuleHandler($this->moduleHandler); } @@ -1258,8 +1252,12 @@ public function testLoadMultipleNoPersistentCache() { $this->cache->expects($this->never()) ->method('set'); + $this->entityTypeManager->expects($this->any()) + ->method('getActiveDefinition') + ->will($this->returnValue($this->entityType)); + $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository]) + ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager]) ->setMethods(['getFromStorage', 'invokeStorageLoadHook', 'initTableLayout']) ->getMock(); $entity_storage->method('invokeStorageLoadHook') @@ -1312,8 +1310,12 @@ public function testLoadMultiplePersistentCacheMiss() { ->method('set') ->with($key, $entity, CacheBackendInterface::CACHE_PERMANENT, [$this->entityTypeId . '_values', 'entity_field_info']); + $this->entityTypeManager->expects($this->any()) + ->method('getActiveDefinition') + ->will($this->returnValue($this->entityType)); + $entity_storage = $this->getMockBuilder('Drupal\Core\Entity\Sql\SqlContentEntityStorage') - ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository]) + ->setConstructorArgs([$this->entityType, $this->connection, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager]) ->setMethods(['getFromStorage', 'invokeStorageLoadHook', 'initTableLayout']) ->getMock(); $entity_storage->method('invokeStorageLoadHook') @@ -1362,15 +1364,19 @@ public function testHasData() { ->method('getDefinition') ->will($this->returnValue($this->entityType)); + $this->entityTypeManager->expects($this->any()) + ->method('getActiveDefinition') + ->will($this->returnValue($this->entityType)); + $this->entityFieldManager->expects($this->any()) ->method('getFieldStorageDefinitions') ->will($this->returnValue($this->fieldDefinitions)); $this->entityFieldManager->expects($this->any()) - ->method('getBaseFieldDefinitions') + ->method('getActiveFieldStorageDefinitions') ->will($this->returnValue($this->fieldDefinitions)); - $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager, $this->entityLastInstalledSchemaRepository); + $this->entityStorage = new SqlContentEntityStorage($this->entityType, $database, $this->entityFieldManager, $this->cache, $this->languageManager, new MemoryCache(), $this->entityTypeBundleInfo, $this->entityTypeManager); $result = $this->entityStorage->hasData(); @@ -1446,6 +1452,18 @@ public function testCleanIds() { } + /** + * @covers ::getFieldStorageDefinitions + * + * @expectedDeprecation 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. + * + * @group legacy + */ + public function testGetFieldStorageDefinitions() { + $this->setUpEntityStorage(); + $this->entityStorage->getFieldStorageDefinitions(); + } + /** * Sets up the module handler with no implementations. */