diff --git a/core/core.services.yml b/core/core.services.yml index a573e2c1bb..5e84779315 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -291,7 +291,7 @@ services: - { name: dynamic_page_cache_response_policy } config.manager: class: Drupal\Core\Config\ConfigManager - arguments: ['@entity.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage', '@event_dispatcher'] + arguments: ['@entity_type.manager', '@config.factory', '@config.typed', '@string_translation', '@config.storage', '@event_dispatcher', '@entity.repository'] config.factory: class: Drupal\Core\Config\ConfigFactory tags: @@ -518,7 +518,7 @@ services: class: Drupal\Core\Entity\ContentUninstallValidator tags: - { name: module_install.uninstall_validator } - arguments: ['@entity.manager', '@string_translation'] + arguments: ['@entity_type.manager', '@string_translation'] lazy: true required_module_uninstall_validator: class: Drupal\Core\Extension\RequiredModuleUninstallValidator @@ -573,12 +573,12 @@ services: arguments: ['@entity_type.manager', '@entity_type.bundle.info', '@entity_field.manager', '@module_handler'] entity_route_subscriber: class: Drupal\Core\EventSubscriber\EntityRouteProviderSubscriber - arguments: ['@entity.manager'] + arguments: ['@entity_type.manager'] tags: - { name: event_subscriber } entity.definition_update_manager: class: Drupal\Core\Entity\EntityDefinitionUpdateManager - arguments: ['@entity.manager', '@entity.last_installed_schema.repository'] + arguments: ['@entity_type.manager', '@entity.last_installed_schema.repository', '@entity_field.manager', '@entity_type.listener', '@field_storage_definition.listener'] entity.last_installed_schema.repository: class: Drupal\Core\Entity\EntityLastInstalledSchemaRepository arguments: ['@keyvalue'] @@ -593,10 +593,10 @@ services: arguments: ['@entity_type.manager', '@entity_field.manager', '@keyvalue', '@cache.discovery'] entity.form_builder: class: Drupal\Core\Entity\EntityFormBuilder - arguments: ['@entity.manager', '@form_builder'] + arguments: ['@entity_type.manager', '@form_builder'] entity.bundle_config_import_validator: class: Drupal\Core\Entity\Event\BundleConfigImportValidate - arguments: ['@config.manager', '@entity.manager'] + arguments: ['@config.manager', '@entity_type.manager'] tags: - { name: event_subscriber } entity.autocomplete_matcher: @@ -643,7 +643,7 @@ services: - { name: needs_destruction } menu.parent_form_selector: class: Drupal\Core\Menu\MenuParentFormSelector - arguments: ['@menu.link_tree', '@entity.manager', '@string_translation'] + arguments: ['@menu.link_tree', '@entity_type.manager', '@string_translation'] plugin.manager.menu.local_action: class: Drupal\Core\Menu\LocalActionManager arguments: ['@http_kernel.controller.argument_resolver', '@request_stack', '@current_route_match', '@router.route_provider', '@module_handler', '@cache.discovery', '@language_manager', '@access_manager', '@current_user'] @@ -903,7 +903,7 @@ services: - { name: event_subscriber } entity.query: class: Drupal\Core\Entity\Query\QueryFactory - arguments: ['@entity.manager'] + arguments: ['@entity_type.manager'] calls: - [setContainer, ['@service_container']] deprecated: The "%service_id%" service is deprecated. Use the 'entity_type.manager' service to get an entity type's storage object and then call \Drupal\Core\Entity\EntityStorageInterface::getQuery() or \Drupal\Core\Entity\EntityStorageInterface::getAggregateQuery() instead. See https://www.drupal.org/node/2849874 @@ -999,7 +999,7 @@ services: class: Drupal\Core\ParamConverter\EntityConverter tags: - { name: paramconverter } - arguments: ['@entity.manager', '@language_manager'] + arguments: ['@entity_type.manager', '@language_manager', '@entity.repository'] paramconverter.entity_revision: class: Drupal\Core\ParamConverter\EntityRevisionParamConverter tags: @@ -1010,7 +1010,7 @@ services: tags: # Use a higher priority than EntityConverter, see the class for details. - { name: paramconverter, priority: 5 } - arguments: ['@entity.manager', '@config.factory', '@router.admin_context'] + arguments: ['@entity_type.manager', '@config.factory', '@router.admin_context', '@language_manager', '@entity.repository'] lazy: true route_subscriber.module: class: Drupal\Core\EventSubscriber\ModuleRouteSubscriber @@ -1019,7 +1019,7 @@ services: arguments: ['@module_handler'] resolver_manager.entity: class: Drupal\Core\Entity\EntityResolverManager - arguments: ['@entity.manager', '@class_resolver'] + arguments: ['@entity_type.manager', '@class_resolver'] route_subscriber.entity: class: Drupal\Core\EventSubscriber\EntityRouteAlterSubscriber tags: @@ -1113,7 +1113,7 @@ services: arguments: ['@http_kernel.controller.argument_resolver', '@form_builder', '@class_resolver'] controller.entity_form: class: Drupal\Core\Entity\HtmlEntityFormController - arguments: ['@http_kernel.controller.argument_resolver', '@form_builder', '@entity.manager'] + arguments: ['@http_kernel.controller.argument_resolver', '@form_builder', '@entity_type.manager'] form_ajax_response_builder: class: Drupal\Core\Form\FormAjaxResponseBuilder arguments: ['@main_content_renderer.ajax', '@current_route_match'] @@ -1157,7 +1157,7 @@ services: - { name: access_check, applies_to: _entity_access } access_check.entity_create: class: Drupal\Core\Entity\EntityCreateAccessCheck - arguments: ['@entity.manager'] + arguments: ['@entity_type.manager'] tags: - { name: access_check, applies_to: _entity_create_access } access_check.entity_create_any: @@ -1412,7 +1412,7 @@ services: arguments: ['@module_handler'] date.formatter: class: Drupal\Core\Datetime\DateFormatter - arguments: ['@entity.manager', '@language_manager', '@string_translation', '@config.factory', '@request_stack'] + arguments: ['@entity_type.manager', '@language_manager', '@string_translation', '@config.factory', '@request_stack'] feed.bridge.reader: class: Drupal\Component\Bridge\ZfExtensionManagerSfContainer calls: diff --git a/core/lib/Drupal/Core/Config/ConfigImporter.php b/core/lib/Drupal/Core/Config/ConfigImporter.php index 0f9eae967c..4dced73e15 100644 --- a/core/lib/Drupal/Core/Config/ConfigImporter.php +++ b/core/lib/Drupal/Core/Config/ConfigImporter.php @@ -867,8 +867,8 @@ protected function checkOp($collection, $op, $name) { // If the target already exists, use the entity storage to delete it // again, if is a simple config, delete it directly. if ($entity_type_id = $this->configManager->getEntityTypeIdByName($name)) { - $entity_storage = $this->configManager->getEntityManager()->getStorage($entity_type_id); - $entity_type = $this->configManager->getEntityManager()->getDefinition($entity_type_id); + $entity_storage = $this->configManager->getEntityTypeManager()->getStorage($entity_type_id); + $entity_type = $this->configManager->getEntityTypeManager()->getDefinition($entity_type_id); $entity = $entity_storage->load($entity_storage->getIDFromConfigName($name, $entity_type->getConfigPrefix())); $entity->delete(); $this->logError($this->t('Deleted and replaced configuration entity "@name"', ['@name' => $name])); @@ -970,7 +970,7 @@ protected function importInvokeOwner($collection, $op, $name) { } $method = 'import' . ucfirst($op); - $entity_storage = $this->configManager->getEntityManager()->getStorage($entity_type); + $entity_storage = $this->configManager->getEntityTypeManager()->getStorage($entity_type); // Call to the configuration entity's storage to handle the configuration // change. if (!($entity_storage instanceof ImportableEntityStorageInterface)) { @@ -1016,7 +1016,7 @@ protected function importInvokeRename($collection, $rename_name) { $new_config->setData($data); } - $entity_storage = $this->configManager->getEntityManager()->getStorage($entity_type_id); + $entity_storage = $this->configManager->getEntityTypeManager()->getStorage($entity_type_id); // Call to the configuration entity's storage to handle the configuration // change. if (!($entity_storage instanceof ImportableEntityStorageInterface)) { diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php index 4a198a7252..6cf244b861 100644 --- a/core/lib/Drupal/Core/Config/ConfigInstaller.php +++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php @@ -326,7 +326,7 @@ protected function createConfiguration($collection, array $config_to_create) { } /** @var \Drupal\Core\Config\Entity\ConfigEntityStorageInterface $entity_storage */ $entity_storage = $this->configManager - ->getEntityManager() + ->getEntityTypeManager() ->getStorage($entity_type); $id = $entity_storage->getIDFromConfigName($name, $entity_storage->getEntityType()->getConfigPrefix()); diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php index e766683069..6725e0cf91 100644 --- a/core/lib/Drupal/Core/Config/ConfigManager.php +++ b/core/lib/Drupal/Core/Config/ConfigManager.php @@ -6,8 +6,9 @@ use Drupal\Core\Config\Entity\ConfigDependencyManager; use Drupal\Core\Config\Entity\ConfigEntityInterface; use Drupal\Core\Config\Entity\ConfigEntityTypeInterface; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; use Drupal\Core\Entity\EntityTypeInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Serialization\Yaml; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; @@ -20,11 +21,18 @@ class ConfigManager implements ConfigManagerInterface { use StringTranslationTrait; /** - * The entity manager. + * The entity type manager. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; + + /** + * The entity repository. + * + * @var \Drupal\Core\Entity\EntityRepositoryInterface + */ + protected $entityRepository; /** * The configuration factory. @@ -71,8 +79,8 @@ class ConfigManager implements ConfigManagerInterface { /** * Creates ConfigManager objects. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The configuration factory. * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config_manager @@ -83,21 +91,24 @@ class ConfigManager implements ConfigManagerInterface { * The active configuration storage. * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher * The event dispatcher. + * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository + * The entity repository. */ - public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, TranslationInterface $string_translation, StorageInterface $active_storage, EventDispatcherInterface $event_dispatcher) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config_manager, TranslationInterface $string_translation, StorageInterface $active_storage, EventDispatcherInterface $event_dispatcher, EntityRepositoryInterface $entity_repository = NULL) { + $this->entityTypeManager = $entity_type_manager; $this->configFactory = $config_factory; $this->typedConfigManager = $typed_config_manager; $this->stringTranslation = $string_translation; $this->activeStorage = $active_storage; $this->eventDispatcher = $event_dispatcher; + $this->entityRepository = $entity_repository ?: \Drupal::service('entity.repository'); } /** * {@inheritdoc} */ public function getEntityTypeIdByName($name) { - $entities = array_filter($this->entityManager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($name) { + $entities = array_filter($this->entityTypeManager->getDefinitions(), function (EntityTypeInterface $entity_type) use ($name) { return ($entity_type instanceof ConfigEntityTypeInterface && $config_prefix = $entity_type->getConfigPrefix()) && strpos($name, $config_prefix . '.') === 0; }); return key($entities); @@ -109,9 +120,9 @@ public function getEntityTypeIdByName($name) { public function loadConfigEntityByName($name) { $entity_type_id = $this->getEntityTypeIdByName($name); if ($entity_type_id) { - $entity_type = $this->entityManager->getDefinition($entity_type_id); + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); $id = substr($name, strlen($entity_type->getConfigPrefix()) + 1); - return $this->entityManager->getStorage($entity_type_id)->load($id); + return $this->entityTypeManager->getStorage($entity_type_id)->load($id); } return NULL; } @@ -120,7 +131,15 @@ public function loadConfigEntityByName($name) { * {@inheritdoc} */ public function getEntityManager() { - return $this->entityManager; + @trigger_error('ConfigManagerInterface::getEntityManager() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Use ::getEntityTypeManager() instead. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); + return \Drupal::service('entity.manager'); + } + + /** + * {@inheritdoc} + */ + public function getEntityTypeManager() { + return $this->entityTypeManager; } /** @@ -264,7 +283,7 @@ public function findConfigEntityDependents($type, array $names, ConfigDependency public function findConfigEntityDependentsAsEntities($type, array $names, ConfigDependencyManager $dependency_manager = NULL) { $dependencies = $this->findConfigEntityDependents($type, $names, $dependency_manager); $entities = []; - $definitions = $this->entityManager->getDefinitions(); + $definitions = $this->entityTypeManager->getDefinitions(); foreach ($dependencies as $config_name => $dependency) { // Group by entity type to efficient load entities using // \Drupal\Core\Entity\EntityStorageInterface::loadMultiple(). @@ -280,7 +299,7 @@ public function findConfigEntityDependentsAsEntities($type, array $names, Config } $entities_to_return = []; foreach ($entities as $entity_type_id => $entities_to_load) { - $storage = $this->entityManager->getStorage($entity_type_id); + $storage = $this->entityTypeManager->getStorage($entity_type_id); // Remove the keys since there are potential ID clashes from different // configuration entity types. $entities_to_return = array_merge($entities_to_return, array_values($storage->loadMultiple($entities_to_load))); @@ -440,7 +459,7 @@ protected function callOnDependencyRemoval(ConfigEntityInterface $entity, array else { // Ignore the bundle. list($entity_type_id,, $uuid) = explode(':', $name); - return $this->entityManager->loadEntityByConfigTarget($entity_type_id, $uuid); + return $this->entityRepository->loadEntityByConfigTarget($entity_type_id, $uuid); } }, $affected_dependencies[$type]); } @@ -487,7 +506,7 @@ public function findMissingContentDependencies() { foreach (array_unique($content_dependencies) as $content_dependency) { // Format of the dependency is entity_type:bundle:uuid. list($entity_type, $bundle, $uuid) = explode(':', $content_dependency, 3); - if (!$this->entityManager->loadEntityByUuid($entity_type, $uuid)) { + if (!$this->entityRepository->loadEntityByUuid($entity_type, $uuid)) { $missing_dependencies[$uuid] = [ 'entity_type' => $entity_type, 'bundle' => $bundle, diff --git a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php index 2340c8a94a..44402b7a5d 100644 --- a/core/lib/Drupal/Core/Config/ConfigManagerInterface.php +++ b/core/lib/Drupal/Core/Config/ConfigManagerInterface.php @@ -34,14 +34,26 @@ public function loadConfigEntityByName($name); * * @return \Drupal\Core\Entity\EntityManagerInterface * The entity manager. + * + * @deprecated in Drupal 8.7.x, will be removed before Drupal 9.0.0. Use + * \Drupal\Core\Config\ConfigManagerInterface::getEntityTypeManager() + * instead. */ public function getEntityManager(); + /** + * Gets the entity type manager. + * + * @return \Drupal\Core\Entity\EntityTypeManagerInterface + * The entity type manager. + */ + public function getEntityTypeManager(); + /** * Gets the config factory. * * @return \Drupal\Core\Config\ConfigFactoryInterface - * The entity manager. + * The config factory. */ public function getConfigFactory(); diff --git a/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php b/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php index d784cdb2c5..bce7bb719b 100644 --- a/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php +++ b/core/lib/Drupal/Core/Config/Entity/Query/QueryFactory.php @@ -226,7 +226,7 @@ public function onConfigSave(ConfigCrudEvent $event) { $saved_config = $event->getConfig(); $entity_type_id = $this->configManager->getEntityTypeIdByName($saved_config->getName()); if ($entity_type_id) { - $entity_type = $this->configManager->getEntityManager()->getDefinition($entity_type_id); + $entity_type = $this->configManager->getEntityTypeManager()->getDefinition($entity_type_id); $this->updateConfigKeyStore($entity_type, $saved_config); } } @@ -241,7 +241,7 @@ public function onConfigDelete(ConfigCrudEvent $event) { $saved_config = $event->getConfig(); $entity_type_id = $this->configManager->getEntityTypeIdByName($saved_config->getName()); if ($entity_type_id) { - $entity_type = $this->configManager->getEntityManager()->getDefinition($entity_type_id); + $entity_type = $this->configManager->getEntityTypeManager()->getDefinition($entity_type_id); $this->deleteConfigKeyStore($entity_type, $saved_config); } } diff --git a/core/lib/Drupal/Core/Datetime/DateFormatter.php b/core/lib/Drupal/Core/Datetime/DateFormatter.php index 7729080352..d784743cd5 100644 --- a/core/lib/Drupal/Core/Datetime/DateFormatter.php +++ b/core/lib/Drupal/Core/Datetime/DateFormatter.php @@ -3,7 +3,7 @@ namespace Drupal\Core\Datetime; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\StringTranslation\TranslationInterface; @@ -78,8 +78,8 @@ class DateFormatter implements DateFormatterInterface { /** * Constructs a Date object. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager * The language manager. * @param \Drupal\Core\StringTranslation\TranslationInterface $translation @@ -89,8 +89,8 @@ class DateFormatter implements DateFormatterInterface { * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack * The request stack. */ - public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, TranslationInterface $translation, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { - $this->dateFormatStorage = $entity_manager->getStorage('date_format'); + public function __construct(EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager, TranslationInterface $translation, ConfigFactoryInterface $config_factory, RequestStack $request_stack) { + $this->dateFormatStorage = $entity_type_manager->getStorage('date_format'); $this->languageManager = $language_manager; $this->stringTranslation = $translation; $this->configFactory = $config_factory; diff --git a/core/lib/Drupal/Core/DependencyInjection/DeprecatedServicePropertyTrait.php b/core/lib/Drupal/Core/DependencyInjection/DeprecatedServicePropertyTrait.php new file mode 100644 index 0000000000..b0681ec0b8 --- /dev/null +++ b/core/lib/Drupal/Core/DependencyInjection/DeprecatedServicePropertyTrait.php @@ -0,0 +1,28 @@ +deprecatedProperties)) { + throw new \LogicException('The deprecatedProperties property must be defined to use this trait.'); + } + + if (isset($this->deprecatedProperties[$name])) { + $service_name = $this->deprecatedProperties[$name]; + $class_name = static::class; + @trigger_error("The property $name ($service_name service) is deprecated in $class_name and will be removed before Drupal 9.0.0.", E_USER_DEPRECATED); + return \Drupal::service($service_name); + } + } + +} diff --git a/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php index 6b90843121..ac76d9a751 100644 --- a/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php +++ b/core/lib/Drupal/Core/Entity/ContentUninstallValidator.php @@ -14,22 +14,22 @@ class ContentUninstallValidator implements ModuleUninstallValidatorInterface { use StringTranslationTrait; /** - * The entity manager. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * Constructs a new ContentUninstallValidator. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The string translation service. */ - public function __construct(EntityManagerInterface $entity_manager, TranslationInterface $string_translation) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) { + $this->entityTypeManager = $entity_type_manager; $this->stringTranslation = $string_translation; } @@ -37,10 +37,10 @@ public function __construct(EntityManagerInterface $entity_manager, TranslationI * {@inheritdoc} */ public function validate($module) { - $entity_types = $this->entityManager->getDefinitions(); + $entity_types = $this->entityTypeManager->getDefinitions(); $reasons = []; foreach ($entity_types as $entity_type) { - if ($module == $entity_type->getProvider() && $entity_type instanceof ContentEntityTypeInterface && $this->entityManager->getStorage($entity_type->id())->hasData()) { + if ($module == $entity_type->getProvider() && $entity_type instanceof ContentEntityTypeInterface && $this->entityTypeManager->getStorage($entity_type->id())->hasData()) { $reasons[] = $this->t('There is content for the entity type: @entity_type. Remove @entity_type_plural.', [ '@entity_type' => $entity_type->getLabel(), '@entity_type_plural' => $entity_type->getPluralLabel(), diff --git a/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php b/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php index b3c12a8da8..f0a359574a 100644 --- a/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php +++ b/core/lib/Drupal/Core/Entity/EntityCreateAccessCheck.php @@ -14,11 +14,11 @@ class EntityCreateAccessCheck implements AccessInterface { /** - * The entity manager. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * The key used by the routing requirement. @@ -30,11 +30,11 @@ class EntityCreateAccessCheck implements AccessInterface { /** * Constructs a EntityCreateAccessCheck object. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. */ - public function __construct(EntityManagerInterface $entity_manager) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager) { + $this->entityTypeManager = $entity_type_manager; } /** @@ -65,7 +65,7 @@ public function access(Route $route, RouteMatchInterface $route_match, AccountIn return AccessResult::neutral(sprintf("Could not find '%s' request argument, therefore cannot check create access.", $bundle)); } } - return $this->entityManager->getAccessControlHandler($entity_type)->createAccess($bundle, $account, [], TRUE); + return $this->entityTypeManager->getAccessControlHandler($entity_type)->createAccess($bundle, $account, [], TRUE); } } diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php index c5d0abd502..aea8a2b275 100644 --- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php +++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManager.php @@ -2,10 +2,12 @@ namespace Drupal\Core\Entity; +use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; use Drupal\Core\Entity\Schema\DynamicallyFieldableEntityStorageSchemaInterface; use Drupal\Core\Entity\Schema\EntityStorageSchemaInterface; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\Field\FieldStorageDefinitionListenerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; /** @@ -13,13 +15,40 @@ */ class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInterface { use StringTranslationTrait; + use DeprecatedServicePropertyTrait; /** - * The entity manager service. + * {@inheritdoc} + */ + protected $deprecatedProperties = ['entityManager' => 'entity.manager']; + + /** + * The entity field manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityFieldManagerInterface */ - protected $entityManager; + protected $entityFieldManager; + + /** + * The entity type listener service. + * + * @var \Drupal\Core\Entity\EntityTypeListenerInterface|\Drupal\Core\Entity\EntityTypeSchemaListenerInterface + */ + protected $entityTypeListener; + + /** + * The entity type manager service. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * The field storage definition listener service. + * + * @var \Drupal\Core\Field\FieldStorageDefinitionListenerInterface + */ + protected $fieldStorageDefinitionListener; /** * The last installed schema repository. @@ -31,19 +60,33 @@ class EntityDefinitionUpdateManager implements EntityDefinitionUpdateManagerInte /** * Constructs a new EntityDefinitionUpdateManager. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. * @param \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository * The last installed schema repository service. + * @param \Drupal\Core\Entity\EntityFieldManagerInterface $entity_field_manager + * The entity field manager service. + * @param \Drupal\Core\Entity\EntityTypeListenerInterface $entity_type_listener + * The entity type listener interface. + * @param \Drupal\Core\Field\FieldStorageDefinitionListenerInterface $field_storage_definition_listener + * The field storage definition listener service. */ - public function __construct(EntityManagerInterface $entity_manager, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository = NULL) { - $this->entityManager = $entity_manager; - - if (!isset($entity_last_installed_schema_repository)) { - @trigger_error('The $entity_last_installed_schema_repository parameter was added in Drupal 8.6.x and will be required in 9.0.0. See https://www.drupal.org/node/2973262.', E_USER_DEPRECATED); - $entity_last_installed_schema_repository = \Drupal::service('entity.last_installed_schema.repository'); + public function __construct(EntityTypeManagerInterface $entity_type_manager, EntityLastInstalledSchemaRepositoryInterface $entity_last_installed_schema_repository = NULL, EntityFieldManagerInterface $entity_field_manager = NULL, EntityTypeListenerInterface $entity_type_listener = NULL, FieldStorageDefinitionListenerInterface $field_storage_definition_listener = NULL) { + if ($entity_type_manager instanceof EntityManagerInterface) { + @trigger_error('Passing the entity.manager service to EntityDefinitionUpdateManager::__construct() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Pass the new dependencies instead. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); + $this->entityTypeManager = \Drupal::entityTypeManager(); + $this->entityFieldManager = \Drupal::service('entity_field.manager'); + $this->entityLastInstalledSchemaRepository = \Drupal::service('entity.last_installed_schema.repository'); + $this->entityTypeListener = \Drupal::service('entity_type.listener'); + $this->fieldStorageDefinitionListener = \Drupal::service('field_storage_definition.listener'); + } + else { + $this->entityTypeManager = $entity_type_manager; + $this->entityFieldManager = $entity_field_manager; + $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository; + $this->entityTypeListener = $entity_type_listener; + $this->fieldStorageDefinitionListener = $field_storage_definition_listener; } - $this->entityLastInstalledSchemaRepository = $entity_last_installed_schema_repository; } /** @@ -62,7 +105,7 @@ public function getChangeSummary() { foreach ($this->getChangeList() as $entity_type_id => $change_list) { // Process entity type definition changes. if (!empty($change_list['entity_type'])) { - $entity_type = $this->entityManager->getDefinition($entity_type_id); + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); switch ($change_list['entity_type']) { case static::DEFINITION_CREATED: @@ -77,7 +120,7 @@ public function getChangeSummary() { // Process field storage definition changes. if (!empty($change_list['field_storage_definitions'])) { - $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); + $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type_id); $original_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type_id); foreach ($change_list['field_storage_definitions'] as $field_name => $change) { @@ -109,7 +152,7 @@ public function applyUpdates() { if ($complete_change_list) { // self::getChangeList() only disables the cache and does not invalidate. // In case there are changes, explicitly invalidate caches. - $this->entityManager->clearCachedDefinitions(); + $this->clearCachedDefinitions(); } foreach ($complete_change_list as $entity_type_id => $change_list) { // Process entity type definition changes before storage definitions ones @@ -122,7 +165,7 @@ public function applyUpdates() { // Process field storage definition changes. if (!empty($change_list['field_storage_definitions'])) { - $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); + $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type_id); $original_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type_id); foreach ($change_list['field_storage_definitions'] as $field_name => $change) { @@ -153,8 +196,8 @@ public function getEntityTypes() { * {@inheritdoc} */ public function installEntityType(EntityTypeInterface $entity_type) { - $this->entityManager->clearCachedDefinitions(); - $this->entityManager->onEntityTypeCreate($entity_type); + $this->clearCachedDefinitions(); + $this->entityTypeListener->onEntityTypeCreate($entity_type); } /** @@ -162,16 +205,30 @@ public function installEntityType(EntityTypeInterface $entity_type) { */ public function updateEntityType(EntityTypeInterface $entity_type) { $original = $this->getEntityType($entity_type->id()); - $this->entityManager->clearCachedDefinitions(); - $this->entityManager->onEntityTypeUpdate($entity_type, $original); + $this->clearCachedDefinitions(); + $this->entityTypeListener->onEntityTypeUpdate($entity_type, $original); } /** * {@inheritdoc} */ public function uninstallEntityType(EntityTypeInterface $entity_type) { - $this->entityManager->clearCachedDefinitions(); - $this->entityManager->onEntityTypeDelete($entity_type); + $this->clearCachedDefinitions(); + $this->entityTypeListener->onEntityTypeDelete($entity_type); + } + + /** + * {@inheritdoc} + */ + public function updateEntityTypeSchema(EntityTypeInterface $entity_type, array $field_storage_definitions, array &$sandbox = NULL) { + $original = $this->getEntityType($entity_type->id()); + + if ($this->requiresEntityDataMigration($entity_type, $original) && $sandbox === NULL) { + throw new \InvalidArgumentException('The entity schema update for the ' . $entity_type->id() . ' entity type requires a data migration.'); + } + + $original_field_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type->id()); + $this->entityTypeListener->onEntityTypeSchemaUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); } /** @@ -187,8 +244,8 @@ public function installFieldStorageDefinition($name, $entity_type_id, $provider, ->setProvider($provider) ->setTargetBundle(NULL); } - $this->entityManager->clearCachedDefinitions(); - $this->entityManager->onFieldStorageDefinitionCreate($storage_definition); + $this->clearCachedDefinitions(); + $this->fieldStorageDefinitionListener->onFieldStorageDefinitionCreate($storage_definition); } /** @@ -204,16 +261,16 @@ public function getFieldStorageDefinition($name, $entity_type_id) { */ public function updateFieldStorageDefinition(FieldStorageDefinitionInterface $storage_definition) { $original = $this->getFieldStorageDefinition($storage_definition->getName(), $storage_definition->getTargetEntityTypeId()); - $this->entityManager->clearCachedDefinitions(); - $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $original); + $this->clearCachedDefinitions(); + $this->fieldStorageDefinitionListener->onFieldStorageDefinitionUpdate($storage_definition, $original); } /** * {@inheritdoc} */ public function uninstallFieldStorageDefinition(FieldStorageDefinitionInterface $storage_definition) { - $this->entityManager->clearCachedDefinitions(); - $this->entityManager->onFieldStorageDefinitionDelete($storage_definition); + $this->clearCachedDefinitions(); + $this->fieldStorageDefinitionListener->onFieldStorageDefinitionDelete($storage_definition); } /** @@ -226,15 +283,15 @@ public function uninstallFieldStorageDefinition(FieldStorageDefinitionInterface * The entity type ID. */ protected function doEntityUpdate($op, $entity_type_id) { - $entity_type = $this->entityManager->getDefinition($entity_type_id); + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); switch ($op) { case static::DEFINITION_CREATED: - $this->entityManager->onEntityTypeCreate($entity_type); + $this->entityTypeListener->onEntityTypeCreate($entity_type); break; case static::DEFINITION_UPDATED: $original = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id); - $this->entityManager->onEntityTypeUpdate($entity_type, $original); + $this->entityTypeListener->onEntityTypeUpdate($entity_type, $original); break; } } @@ -253,15 +310,15 @@ protected function doEntityUpdate($op, $entity_type_id) { protected function doFieldUpdate($op, $storage_definition = NULL, $original_storage_definition = NULL) { switch ($op) { case static::DEFINITION_CREATED: - $this->entityManager->onFieldStorageDefinitionCreate($storage_definition); + $this->fieldStorageDefinitionListener->onFieldStorageDefinitionCreate($storage_definition); break; case static::DEFINITION_UPDATED: - $this->entityManager->onFieldStorageDefinitionUpdate($storage_definition, $original_storage_definition); + $this->fieldStorageDefinitionListener->onFieldStorageDefinitionUpdate($storage_definition, $original_storage_definition); break; case static::DEFINITION_DELETED: - $this->entityManager->onFieldStorageDefinitionDelete($original_storage_definition); + $this->fieldStorageDefinitionListener->onFieldStorageDefinitionDelete($original_storage_definition); break; } } @@ -280,10 +337,11 @@ protected function doFieldUpdate($op, $storage_definition = NULL, $original_stor * - DEFINITION_DELETED */ protected function getChangeList() { - $this->entityManager->useCaches(FALSE); + $this->entityTypeManager->useCaches(FALSE); + $this->entityFieldManager->useCaches(FALSE); $change_list = []; - foreach ($this->entityManager->getDefinitions() as $entity_type_id => $entity_type) { + foreach ($this->entityTypeManager->getDefinitions() as $entity_type_id => $entity_type) { $original = $this->entityLastInstalledSchemaRepository->getLastInstalledDefinition($entity_type_id); // @todo Support non-storage-schema-changing definition updates too: @@ -296,9 +354,9 @@ protected function getChangeList() { $change_list[$entity_type_id]['entity_type'] = static::DEFINITION_UPDATED; } - if ($this->entityManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) { + if ($this->entityTypeManager->getStorage($entity_type_id) instanceof DynamicallyFieldableEntityStorageInterface) { $field_changes = []; - $storage_definitions = $this->entityManager->getFieldStorageDefinitions($entity_type_id); + $storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($entity_type_id); $original_storage_definitions = $this->entityLastInstalledSchemaRepository->getLastInstalledFieldStorageDefinitions($entity_type_id); // Detect created field storage definitions. @@ -336,7 +394,8 @@ protected function getChangeList() { // purging. // @see https://www.drupal.org/node/2907779 - $this->entityManager->useCaches(TRUE); + $this->entityTypeManager->useCaches(TRUE); + $this->entityFieldManager->useCaches(TRUE); return array_filter($change_list); } @@ -353,7 +412,7 @@ protected function getChangeList() { * TRUE if storage schema changes are required, FALSE otherwise. */ protected function requiresEntityStorageSchemaChanges(EntityTypeInterface $entity_type, EntityTypeInterface $original) { - $storage = $this->entityManager->getStorage($entity_type->id()); + $storage = $this->entityTypeManager->getStorage($entity_type->id()); return ($storage instanceof EntityStorageSchemaInterface) && $storage->requiresEntityStorageSchemaChanges($entity_type, $original); } @@ -369,8 +428,32 @@ protected function requiresEntityStorageSchemaChanges(EntityTypeInterface $entit * TRUE if storage schema changes are required, FALSE otherwise. */ protected function requiresFieldStorageSchemaChanges(FieldStorageDefinitionInterface $storage_definition, FieldStorageDefinitionInterface $original) { - $storage = $this->entityManager->getStorage($storage_definition->getTargetEntityTypeId()); + $storage = $this->entityTypeManager->getStorage($storage_definition->getTargetEntityTypeId()); return ($storage instanceof DynamicallyFieldableEntityStorageSchemaInterface) && $storage->requiresFieldStorageSchemaChanges($storage_definition, $original); } + /** + * Checks if existing data would be lost if the schema changes were applied. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The updated entity type definition. + * @param \Drupal\Core\Entity\EntityTypeInterface $original + * The original entity type definition. + * + * @return bool + * TRUE if data migration is required, FALSE otherwise. + */ + protected function requiresEntityDataMigration(EntityTypeInterface $entity_type, EntityTypeInterface $original) { + $storage = $this->entityTypeManager->getStorage($entity_type->id()); + return ($storage instanceof EntityStorageSchemaInterface) && $storage->requiresEntityDataMigration($entity_type, $original); + } + + /** + * Clears necessary caches to apply entity/field definition updates. + */ + protected function clearCachedDefinitions() { + $this->entityTypeManager->clearCachedDefinitions(); + $this->entityFieldManager->clearCachedFieldDefinitions(); + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php index a219f3c046..3494b607bf 100644 --- a/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php +++ b/core/lib/Drupal/Core/Entity/EntityDefinitionUpdateManagerInterface.php @@ -144,6 +144,20 @@ public function updateEntityType(EntityTypeInterface $entity_type); */ public function uninstallEntityType(EntityTypeInterface $entity_type); + /** + * Applies any schema change performed to the passed entity type definition. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The updated entity type definition. + * @param \Drupal\Core\Field\FieldStorageDefinitionInterface[] $field_storage_definitions + * The updated field storage definitions, including possibly new ones. + * @param array &$sandbox + * (optional) A sandbox array provided by a hook_update_N() implementation + * or a Batch API callback. If the entity schema update requires a data + * migration, this parameter is mandatory. Defaults to NULL. + */ + public function updateEntityTypeSchema(EntityTypeInterface $entity_type, array $field_storage_definitions, array &$sandbox = NULL); + /** * Returns a field storage definition ready to be manipulated. * diff --git a/core/lib/Drupal/Core/Entity/EntityFormBuilder.php b/core/lib/Drupal/Core/Entity/EntityFormBuilder.php index d5f0baf00f..b414460795 100644 --- a/core/lib/Drupal/Core/Entity/EntityFormBuilder.php +++ b/core/lib/Drupal/Core/Entity/EntityFormBuilder.php @@ -11,11 +11,11 @@ class EntityFormBuilder implements EntityFormBuilderInterface { /** - * The entity manager. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * The form builder. @@ -27,13 +27,13 @@ class EntityFormBuilder implements EntityFormBuilderInterface { /** * Constructs a new EntityFormBuilder. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The form builder. */ - public function __construct(EntityManagerInterface $entity_manager, FormBuilderInterface $form_builder) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager, FormBuilderInterface $form_builder) { + $this->entityTypeManager = $entity_type_manager; $this->formBuilder = $form_builder; } @@ -41,7 +41,7 @@ public function __construct(EntityManagerInterface $entity_manager, FormBuilderI * {@inheritdoc} */ public function getForm(EntityInterface $entity, $operation = 'default', array $form_state_additions = []) { - $form_object = $this->entityManager->getFormObject($entity->getEntityTypeId(), $operation); + $form_object = $this->entityTypeManager->getFormObject($entity->getEntityTypeId(), $operation); $form_object->setEntity($entity); $form_state = (new FormState())->setFormState($form_state_additions); diff --git a/core/lib/Drupal/Core/Entity/EntityResolverManager.php b/core/lib/Drupal/Core/Entity/EntityResolverManager.php index e9a87d6a6e..c10d72f551 100644 --- a/core/lib/Drupal/Core/Entity/EntityResolverManager.php +++ b/core/lib/Drupal/Core/Entity/EntityResolverManager.php @@ -14,11 +14,11 @@ class EntityResolverManager { /** - * The entity manager. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * The class resolver. @@ -30,13 +30,13 @@ class EntityResolverManager { /** * Constructs a new EntityRouteAlterSubscriber. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. * @param \Drupal\Core\DependencyInjection\ClassResolverInterface $class_resolver * The class resolver. */ - public function __construct(EntityManagerInterface $entity_manager, ClassResolverInterface $class_resolver) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager, ClassResolverInterface $class_resolver) { + $this->entityTypeManager = $entity_type_manager; $this->classResolver = $class_resolver; } @@ -221,7 +221,7 @@ public function setRouteOptions(Route $route) { */ protected function getEntityTypes() { if (!isset($this->entityTypes)) { - $this->entityTypes = $this->entityManager->getDefinitions(); + $this->entityTypes = $this->entityTypeManager->getDefinitions(); } return $this->entityTypes; } diff --git a/core/lib/Drupal/Core/Entity/EntityTypeListener.php b/core/lib/Drupal/Core/Entity/EntityTypeListener.php index 4dc1e9cf32..c8b23f660a 100644 --- a/core/lib/Drupal/Core/Entity/EntityTypeListener.php +++ b/core/lib/Drupal/Core/Entity/EntityTypeListener.php @@ -9,7 +9,7 @@ * * @see \Drupal\Core\Entity\EntityTypeEvents */ -class EntityTypeListener implements EntityTypeListenerInterface { +class EntityTypeListener implements EntityTypeListenerInterface, EntityTypeSchemaListenerInterface { /** * The entity type manager. @@ -115,4 +115,27 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) { $this->entityLastInstalledSchemaRepository->deleteLastInstalledDefinition($entity_type_id); } + /** + * {@inheritdoc} + */ + public function onEntityTypeSchemaUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) { + $entity_type_id = $entity_type->id(); + + // @todo Forward this to all interested handlers, not only storage, once + // iterating handlers is possible: https://www.drupal.org/node/2332857. + $storage = $this->entityTypeManager->createHandlerInstance($entity_type->getStorageClass(), $entity_type); + if ($storage instanceof EntityTypeSchemaListenerInterface) { + $storage->onEntityTypeSchemaUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); + } + + if ($sandbox === NULL || (isset($sandbox['#finished']) && $sandbox['#finished'] === 1)) { + $this->eventDispatcher->dispatch(EntityTypeEvents::UPDATE, new EntityTypeEvent($entity_type, $original)); + + $this->entityLastInstalledSchemaRepository->setLastInstalledDefinition($entity_type); + if ($entity_type->entityClassImplements(FieldableEntityInterface::class)) { + $this->entityLastInstalledSchemaRepository->setLastInstalledFieldStorageDefinitions($entity_type_id, $field_storage_definitions); + } + } + } + } diff --git a/core/lib/Drupal/Core/Entity/EntityTypeSchemaListenerInterface.php b/core/lib/Drupal/Core/Entity/EntityTypeSchemaListenerInterface.php new file mode 100644 index 0000000000..d05310451b --- /dev/null +++ b/core/lib/Drupal/Core/Entity/EntityTypeSchemaListenerInterface.php @@ -0,0 +1,28 @@ +configManager = $config_manager; - $this->entityManager = $entity_manager; + $this->entityTypeManager = $entity_type_manager; } /** @@ -51,19 +51,19 @@ public function onConfigImporterValidate(ConfigImporterEvent $event) { // Get the config entity type ID. This also ensure we are dealing with a // configuration entity. if ($entity_type_id = $this->configManager->getEntityTypeIdByName($config_name)) { - $entity_type = $this->entityManager->getDefinition($entity_type_id); + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); // Does this entity type define a bundle of another entity type. if ($bundle_of = $entity_type->getBundleOf()) { // Work out if there are entities with this bundle. - $bundle_of_entity_type = $this->entityManager->getDefinition($bundle_of); + $bundle_of_entity_type = $this->entityTypeManager->getDefinition($bundle_of); $bundle_id = ConfigEntityStorage::getIDFromConfigName($config_name, $entity_type->getConfigPrefix()); - $entity_query = $this->entityManager->getStorage($bundle_of)->getQuery(); + $entity_query = $this->entityTypeManager->getStorage($bundle_of)->getQuery(); $entity_ids = $entity_query->condition($bundle_of_entity_type->getKey('bundle'), $bundle_id) ->accessCheck(FALSE) ->range(0, 1) ->execute(); if (!empty($entity_ids)) { - $entity = $this->entityManager->getStorage($entity_type_id)->load($bundle_id); + $entity = $this->entityTypeManager->getStorage($entity_type_id)->load($bundle_id); $event->getConfigImporter()->logError($this->t('Entities exist of type %entity_type and %bundle_label %bundle. These entities need to be deleted before importing.', ['%entity_type' => $bundle_of_entity_type->getLabel(), '%bundle_label' => $bundle_of_entity_type->getBundleLabel(), '%bundle' => $entity->label()])); } } diff --git a/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php b/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php index 586938eb18..f431bc17c0 100644 --- a/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php +++ b/core/lib/Drupal/Core/Entity/HtmlEntityFormController.php @@ -13,11 +13,11 @@ class HtmlEntityFormController extends FormController { /** - * The entity manager service. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * Constructs a new \Drupal\Core\Routing\Enhancer\FormEnhancer object. @@ -26,12 +26,12 @@ class HtmlEntityFormController extends FormController { * The argument resolver. * @param \Drupal\Core\Form\FormBuilderInterface $form_builder * The form builder. - * @param \Drupal\Core\Entity\EntityManagerInterface $manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. */ - public function __construct(ArgumentResolverInterface $argument_resolver, FormBuilderInterface $form_builder, EntityManagerInterface $manager) { + public function __construct(ArgumentResolverInterface $argument_resolver, FormBuilderInterface $form_builder, EntityTypeManagerInterface $entity_type_manager) { parent::__construct($argument_resolver, $form_builder); - $this->entityManager = $manager; + $this->entityTypeManager = $entity_type_manager; } /** @@ -66,7 +66,7 @@ protected function getFormObject(RouteMatchInterface $route_match, $form_arg) { $form_arg .= '.default'; list ($entity_type_id, $operation) = explode('.', $form_arg); - $form_object = $this->entityManager->getFormObject($entity_type_id, $operation); + $form_object = $this->entityTypeManager->getFormObject($entity_type_id, $operation); // Allow the entity form to determine the entity object from a given route // match. diff --git a/core/lib/Drupal/Core/Entity/Query/QueryFactory.php b/core/lib/Drupal/Core/Entity/Query/QueryFactory.php index 79e80cd1f3..45b81e9fa5 100644 --- a/core/lib/Drupal/Core/Entity/Query/QueryFactory.php +++ b/core/lib/Drupal/Core/Entity/Query/QueryFactory.php @@ -4,7 +4,7 @@ @trigger_error('The ' . __NAMESPACE__ . '\QueryFactory class is deprecated in Drupal 8.3.0, will be removed before Drupal 9.0.0. Use \Drupal\Core\Entity\EntityStorageInterface::getQuery() or \Drupal\Core\Entity\EntityStorageInterface::getAggregateQuery() instead. See https://www.drupal.org/node/2849874.', E_USER_DEPRECATED); -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Symfony\Component\DependencyInjection\ContainerAwareInterface; use Symfony\Component\DependencyInjection\ContainerAwareTrait; @@ -26,20 +26,20 @@ class QueryFactory implements ContainerAwareInterface { use ContainerAwareTrait; /** - * Stores the entity manager used by the query. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * Constructs a QueryFactory object. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager used by the query. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. */ - public function __construct(EntityManagerInterface $entity_manager) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager) { + $this->entityTypeManager = $entity_type_manager; } /** @@ -55,7 +55,7 @@ public function __construct(EntityManagerInterface $entity_manager) { * The query object that can query the given entity type. */ public function get($entity_type_id, $conjunction = 'AND') { - return $this->entityManager->getStorage($entity_type_id)->getQuery($conjunction); + return $this->entityTypeManager->getStorage($entity_type_id)->getQuery($conjunction); } /** @@ -71,7 +71,7 @@ public function get($entity_type_id, $conjunction = 'AND') { * The aggregated query object that can query the given entity type. */ public function getAggregate($entity_type_id, $conjunction = 'AND') { - return $this->entityManager->getStorage($entity_type_id)->getAggregateQuery($conjunction); + return $this->entityTypeManager->getStorage($entity_type_id)->getAggregateQuery($conjunction); } } diff --git a/core/lib/Drupal/Core/Entity/Schema/EntityStorageSchemaInterface.php b/core/lib/Drupal/Core/Entity/Schema/EntityStorageSchemaInterface.php index 4feab7b62e..72f1682851 100644 --- a/core/lib/Drupal/Core/Entity/Schema/EntityStorageSchemaInterface.php +++ b/core/lib/Drupal/Core/Entity/Schema/EntityStorageSchemaInterface.php @@ -4,6 +4,7 @@ use Drupal\Core\Entity\EntityTypeInterface; use Drupal\Core\Entity\EntityTypeListenerInterface; +use Drupal\Core\Entity\EntityTypeSchemaListenerInterface; /** * Defines the interface for entity storage schema handler classes. @@ -18,7 +19,7 @@ * * @see \Drupal\Core\Entity\EntityStorageInterface */ -interface EntityStorageSchemaInterface extends EntityTypeListenerInterface { +interface EntityStorageSchemaInterface extends EntityTypeListenerInterface, EntityTypeSchemaListenerInterface { /** * Checks if the changes to the entity type requires storage schema changes. diff --git a/core/lib/Drupal/Core/Entity/Sql/EntityTypeSchemaListenerTrait.php b/core/lib/Drupal/Core/Entity/Sql/EntityTypeSchemaListenerTrait.php new file mode 100644 index 0000000000..2b8a46562d --- /dev/null +++ b/core/lib/Drupal/Core/Entity/Sql/EntityTypeSchemaListenerTrait.php @@ -0,0 +1,246 @@ +entityManager->createHandlerInstance($original->getStorageClass(), $original); + $has_data = $original_storage->hasData(); + + // We cannot support updating the schema of an entity type from revisionable + // to non-revisionable or translatable to non-translatable because that + // implies data loss. + $convert_rev_to_non_rev = $original->isRevisionable() && !$entity_type->isRevisionable(); + $convert_mul_to_non_mul = $original->isTranslatable() && !$entity_type->isTranslatable(); + if ($has_data && ($convert_rev_to_non_rev || $convert_mul_to_non_mul)) { + throw new EntityStorageException('Converting an entity type from revisionable to non-revisionable or from translatable to non-translatable is not supported.'); + } + + // Check that the fields required by a revisionable entity type exist. + if ($entity_type->isRevisionable() && !isset($field_storage_definitions[$entity_type->getKey('revision')])) { + throw new EntityStorageException('Missing revision field.'); + } + if ($entity_type->isRevisionable() && !isset($field_storage_definitions[$entity_type->getRevisionMetadataKey('revision_default')])) { + throw new EntityStorageException('Missing revision_default field.'); + } + + // Check that the fields required by a translatable entity type exist. + if ($entity_type->isTranslatable() && !isset($field_storage_definitions[$entity_type->getKey('langcode')])) { + throw new EntityStorageException('Missing langcode field.'); + } + if ($entity_type->isTranslatable() && !isset($field_storage_definitions[$entity_type->getKey('default_langcode')])) { + throw new EntityStorageException('Missing default_langcode field.'); + } + + // Check that the fields required by a revisionable and translatable entity + // type exist. + if ($entity_type->isRevisionable() && $entity_type->isTranslatable() && !isset($field_storage_definitions[$entity_type->getKey('revision_translation_affected')])) { + throw new EntityStorageException('Missing revision_translation_affected field.'); + } + + $this->fieldStorageDefinitions = $field_storage_definitions; + + // If 'progress' is not set, then this will be the first run of the batch. + if (!isset($sandbox['progress'])) { + $sandbox['original_storage'] = $original_storage; + $sandbox['temporary_storage'] = $this->entityManager->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); + } + else { + // If there is no existing data, we still need to run the + // post-schema-update tasks. + $sandbox['#finished'] = 1; + } + + // If the data copying has finished successfully, allow the storage schema + // to do any required cleanup tasks. For example, this process should take + // care of transforming the temporary storage into the current storage. + if ($sandbox['#finished'] === 1) { + $this->postUpdateEntityTypeSchema($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); + } + } + + /** + * Allows subscribers to prepare their schema before data copying. + * + * @param \Drupal\Core\Entity\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 + * (optional) A sandbox array provided by a hook_update_N() implementation + * or a Batch API callback. If the entity schema update requires a data + * migration, this parameter is mandatory. Defaults to NULL. + */ + abstract protected function preUpdateEntityTypeSchema(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL); + + /** + * Allows subscribers to do any cleanup necessary after data copying. + * + * @param \Drupal\Core\Entity\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 + * (optional) A sandbox array provided by a hook_update_N() implementation + * or a Batch API callback. If the entity schema update requires a data + * migration, this parameter is mandatory. Defaults to NULL. + */ + abstract protected function postUpdateEntityTypeSchema(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL); + + /** + * Copies entity data from the original storage to the temporary one. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The updated entity type definition. + * @param \Drupal\Core\Entity\EntityTypeInterface $original + * The original entity type definition. + * @param array &$sandbox + * The sandbox array from a hook_update_N() implementation. + */ + protected function copyData(EntityTypeInterface $entity_type, EntityTypeInterface $original, array &$sandbox) { + /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ + $id_key = $entity_type->getKey('id'); + $revision_id_key = $entity_type->getKey('revision'); + $revision_default_key = $entity_type->getRevisionMetadataKey('revision_default'); + $langcode_key = $entity_type->getKey('langcode'); + $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; + $sandbox['current_id'] = -1; + } + + // If the original entity type is revisionable, we need to copy all the + // revisions. + $load_revisions = $original->isRevisionable(); + if ($load_revisions) { + $table_name = $original->getRevisionTable(); + $identifier_field = $revision_id_key; + } + else { + $table_name = $original->getBaseTable(); + $identifier_field = $id_key; + } + + // Get the next entity identifiers to migrate. + // @todo Use an entity query when it is able to use the last installed + // entity type and field storage definitions. + // @see https://www.drupal.org/project/drupal/issues/2554235 + $step_size = Settings::get('entity_update_batch_size', 50); + $entity_identifiers = $this->database->select($table_name, 't') + ->condition("t.$identifier_field", $sandbox['current_id'], '>') + ->fields('t', [$identifier_field]) + ->orderBy($identifier_field, 'ASC') + ->range(0, $step_size) + ->execute() + ->fetchCol(); + + /** @var \Drupal\Core\Entity\ContentEntityInterface[] $entities */ + $entities = $load_revisions ? $original_storage->loadMultipleRevisions($entity_identifiers) : $original_storage->loadMultiple($entity_identifiers); + foreach ($entities as $identifier => $entity) { + try { + if (!$original->isRevisionable() && $entity_type->isRevisionable()) { + // Set the revision ID to be same as the entity ID. + $entity->set($revision_id_key, $entity->id()); + + // We had no revisions so far, so the existing data belongs to the + // default revision now. + $entity->set($revision_default_key, TRUE); + } + + // Set the 'langcode' and 'default_langcode' values as needed. + if (!$original->isTranslatable() && $entity_type->isTranslatable()) { + if ($entity->get($langcode_key)->isEmpty()) { + $entity->set($langcode_key, \Drupal::languageManager()->getDefaultLanguage()->getId()); + } + + $entity->set($default_langcode_key, TRUE); + } + + // Set the 'revision_translation_affected' flag to TRUE to match the + // previous API return value: if the field was not defined the value + // returned was always TRUE. + if (!$original->isRevisionable() && !$original->isTranslatable() && $entity_type->isRevisionable() && $entity_type->isTranslatable()) { + $entity->set($revision_translation_affected_key, TRUE); + } + + // Treat the entity as new in order to make the storage do an INSERT + // rather than an UPDATE. + $entity->enforceIsNew(TRUE); + + // Finally, save the entity in the temporary storage. + $temporary_storage->save($entity); + } + catch (\Exception $e) { + $this->handleEntityTypeSchemaUpdateExceptionOnDataCopy($entity_type, $original, $sandbox); + + // Re-throw the original exception with a helpful message. + $error_revision_id = !$load_revisions ?: ", revision ID: {$entity->getLoadedRevisionId()}"; + throw new EntityStorageException("The entity update process failed while processing the entity type {$entity_type->id()}, ID: {$entity->id()}$error_revision_id.", $e->getCode(), $e); + } + + $sandbox['progress']++; + $sandbox['current_id'] = $identifier; + } + + // If we're not in maintenance mode, the number of entities could change at + // any time so make sure that we always use the latest record count. + $sandbox['max'] = (int) $this->database->select($table_name, 't')->countQuery()->execute()->fetchField(); + $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); + } + + /** + * Handles the case when an error occurs during the data copying step. + * + * @param \Drupal\Core\Entity\EntityTypeInterface $entity_type + * The updated entity type definition. + * @param \Drupal\Core\Entity\EntityTypeInterface $original + * The original entity type definition. + * @param array &$sandbox + * The sandbox array from a hook_update_N() implementation. + */ + protected function handleEntityTypeSchemaUpdateExceptionOnDataCopy(EntityTypeInterface $entity_type, EntityTypeInterface $original, array &$sandbox) { + } + +} diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php index bcbb2e6b9f..3c3f3664f6 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorage.php @@ -1415,6 +1415,15 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) { }); } + /** + * {@inheritdoc} + */ + public function onEntityTypeSchemaUpdate(EntityTypeInterface $entity_type, EntityTypeInterface $original, array $field_storage_definitions, array $original_field_storage_definitions, array &$sandbox = NULL) { + $this->wrapSchemaException(function () use ($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, &$sandbox) { + $this->getStorageSchema()->onEntityTypeSchemaUpdate($entity_type, $original, $field_storage_definitions, $original_field_storage_definitions, $sandbox); + }); + } + /** * {@inheritdoc} */ diff --git a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php index f4505bdbbf..b446b72a5d 100644 --- a/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php +++ b/core/lib/Drupal/Core/Entity/Sql/SqlContentEntityStorageSchema.php @@ -28,6 +28,7 @@ class SqlContentEntityStorageSchema implements DynamicallyFieldableEntityStorageSchemaInterface { use DependencySerializationTrait; + use EntityTypeSchemaListenerTrait; /** * The entity manager. @@ -408,6 +409,132 @@ public function onEntityTypeDelete(EntityTypeInterface $entity_type) { $this->deleteEntitySchemaData($entity_type); } + /** + * {@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_table_mapping = $temporary_storage->getCustomTableMapping($entity_type, $field_storage_definitions, 'tmp_'); + $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['current_table_mapping'] = $temporary_storage->getCustomTableMapping($entity_type, $field_storage_definitions); + $sandbox['backup_table_mapping'] = $original_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(); + + // Create entity tables. + $temporary_table_names = array_combine($sandbox['current_table_mapping']->getTableNames(), $temporary_table_mapping->getTableNames()); + $schema = $this->getEntitySchema($entity_type, TRUE); + foreach ($schema as $table_name => $table_schema) { + $temp_table_name = $temporary_table_names[$table_name]; + if ($temp_table_name && !$schema_handler->tableExists($temp_table_name)) { + $schema_handler->createTable($temp_table_name, $table_schema); + } + } + + // Create dedicated field tables. + foreach ($field_storage_definitions as $field_storage_definition) { + if ($temporary_table_mapping->requiresDedicatedTableStorage($field_storage_definition)) { + $schema = $this->getDedicatedTableSchema($field_storage_definition); + foreach ($schema as $table_name => $table_schema) { + $temp_table_name = $temporary_table_names[$table_name]; + if ($temp_table_name && !$schema_handler->tableExists($temp_table_name)) { + $schema_handler->createTable($temp_table_name, $table_schema); + } + } + } + } + } + + /** + * {@inheritdoc} + */ + 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(); + /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $original_table_mapping */ + $original_table_mapping = $sandbox['original_storage']->getTableMapping(); + /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $current_table_mapping */ + $current_table_mapping = $sandbox['current_table_mapping']; + /** @var \Drupal\Core\Entity\Sql\TableMappingInterface $backup_table_mapping */ + $backup_table_mapping = $sandbox['backup_table_mapping']; + + // Rename the original tables so we can put them back in place in case + // anything goes wrong. + $backup_table_names = array_combine($original_table_mapping->getTableNames(), $backup_table_mapping->getTableNames()); + foreach ($backup_table_names as $original_table_name => $backup_table_name) { + $this->database->schema()->renameTable($original_table_name, $backup_table_name); + } + + // Put the new tables in place and update the entity type and field storage + // definitions. + try { + $table_name_mapping = array_combine($temporary_table_mapping->getTableNames(), $current_table_mapping->getTableNames()); + foreach ($table_name_mapping as $temp_table_name => $current_table_name) { + $this->database->schema()->renameTable($temp_table_name, $current_table_name); + } + + // Store the updated entity schema. + $entity_schema = $this->getEntitySchema($entity_type, TRUE); + $this->saveEntitySchemaData($entity_type, $entity_schema); + + // Store the updated field schema for each field storage. + foreach ($field_storage_definitions as $field_storage_definition) { + if ($current_table_mapping->requiresDedicatedTableStorage($field_storage_definition)) { + $this->createDedicatedTableSchema($field_storage_definition, TRUE); + } + elseif ($current_table_mapping->allowsSharedTableStorage($field_storage_definition)) { + // The shared tables are already fully created, but we need to save the + // per-field schema definitions for later use. + $this->createSharedTableSchema($field_storage_definition, TRUE); + } + } + } + catch (\Exception $e) { + // Something went wrong, bring back the original tables. + foreach ($backup_table_names as $original_table_name => $backup_table_name) { + // We are in the 'original data recovery' phase, so we need to be sure + // that the initial tables can be properly restored. + if ($this->database->schema()->tableExists($original_table_name)) { + $this->database->schema()->dropTable($original_table_name); + } + + $this->database->schema()->renameTable($backup_table_name, $original_table_name); + } + + // Re-throw the original exception. + throw $e; + } + + // At this point the update process either finished successfully or any + // error has been thrown already, so we can drop the backup entity tables. + foreach ($backup_table_names as $original_table_name => $backup_table_name) { + $this->database->schema()->dropTable($backup_table_name); + } + } + + /** + * {@inheritdoc} + */ + protected function handleEntityTypeSchemaUpdateExceptionOnDataCopy(EntityTypeInterface $entity_type, EntityTypeInterface $original, array &$sandbox) { + // 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(); + foreach ($temporary_table_mapping->getTableNames() as $table_name) { + $this->database->schema()->dropTable($table_name); + } + } + /** * {@inheritdoc} */ @@ -657,8 +784,6 @@ protected function checkEntityType(EntityTypeInterface $entity_type) { * @return array * A Schema API array describing the entity schema, excluding dedicated * field tables. - * - * @throws \Drupal\Core\Field\FieldException */ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $reset = FALSE) { $this->checkEntityType($entity_type); @@ -666,7 +791,9 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res if (!isset($this->schema[$entity_type_id]) || $reset) { // Prepare basic information about the entity type. - $tables = $this->getEntitySchemaTables(); + /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ + $table_mapping = $this->storage->getCustomTableMapping($entity_type, $this->fieldStorageDefinitions); + $tables = $this->getEntitySchemaTables($table_mapping); // Initialize the table schema. $schema[$tables['base_table']] = $this->initializeBaseTable($entity_type); @@ -681,7 +808,6 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res } // We need to act only on shared entity schema tables. - $table_mapping = $this->storage->getCustomTableMapping($entity_type, $this->fieldStorageDefinitions); $table_names = array_diff($table_mapping->getTableNames(), $table_mapping->getDedicatedTableNames()); foreach ($table_names as $table_name) { if (!isset($schema[$table_name])) { @@ -739,15 +865,19 @@ protected function getEntitySchema(ContentEntityTypeInterface $entity_type, $res /** * Gets a list of entity type tables. * + * @param \Drupal\Core\Entity\Sql\TableMappingInterface $table_mapping + * A table mapping object. + * * @return array * A list of entity type tables, keyed by table key. */ - protected function getEntitySchemaTables() { + protected function getEntitySchemaTables(TableMappingInterface $table_mapping) { + /** @var \Drupal\Core\Entity\Sql\DefaultTableMapping $table_mapping */ return array_filter([ - 'base_table' => $this->storage->getBaseTable(), - 'revision_table' => $this->storage->getRevisionTable(), - 'data_table' => $this->storage->getDataTable(), - 'revision_data_table' => $this->storage->getRevisionDataTable(), + 'base_table' => $table_mapping->getBaseTable(), + 'revision_table' => $table_mapping->getRevisionTable(), + 'data_table' => $table_mapping->getDataTable(), + 'revision_data_table' => $table_mapping->getRevisionDataTable(), ]); } @@ -1293,14 +1423,22 @@ protected function performFieldSchemaOperation($operation, FieldStorageDefinitio * * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition * The storage definition of the field being created. + * @param bool $only_save + * (optional) Whether to skip modification of database tables and only save + * the schema data for future comparison. For internal use only. This is + * used by postUpdateEntityTypeSchema() after it has already fully created + * the dedicated tables. */ - protected function createDedicatedTableSchema(FieldStorageDefinitionInterface $storage_definition) { + protected function createDedicatedTableSchema(FieldStorageDefinitionInterface $storage_definition, $only_save = FALSE) { $schema = $this->getDedicatedTableSchema($storage_definition); - foreach ($schema as $name => $table) { - // Check if the table exists because it might already have been - // created as part of the earlier entity type update event. - if (!$this->database->schema()->tableExists($name)) { - $this->database->schema()->createTable($name, $table); + + if (!$only_save) { + foreach ($schema as $name => $table) { + // Check if the table exists because it might already have been + // created as part of the earlier entity type update event. + if (!$this->database->schema()->tableExists($name)) { + $this->database->schema()->createTable($name, $table); + } } } $this->saveFieldSchemaData($storage_definition, $schema); @@ -1956,8 +2094,8 @@ protected function addSharedTableFieldForeignKey(FieldStorageDefinitionInterface * @param \Drupal\Core\Field\FieldStorageDefinitionInterface $storage_definition * The field storage definition. * @param \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type - * (optional) The entity type definition. Defaults to the one returned by - * the entity manager. + * (optional) The entity type definition. Defaults to the one provided by + * the entity storage. * * @return array * The schema definition for the table with the following keys: @@ -1972,10 +2110,11 @@ protected function addSharedTableFieldForeignKey(FieldStorageDefinitionInterface * @see hook_schema() */ protected function getDedicatedTableSchema(FieldStorageDefinitionInterface $storage_definition, ContentEntityTypeInterface $entity_type = NULL) { + $entity_type = $entity_type ?: $this->entityType; $description_current = "Data storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}."; $description_revision = "Revision archive storage for {$storage_definition->getTargetEntityTypeId()} field {$storage_definition->getName()}."; - $id_definition = $this->fieldStorageDefinitions[$this->entityType->getKey('id')]; + $id_definition = $this->fieldStorageDefinitions[$entity_type->getKey('id')]; if ($id_definition->getType() == 'integer') { $id_schema = [ 'type' => 'int', @@ -1994,11 +2133,11 @@ protected function getDedicatedTableSchema(FieldStorageDefinitionInterface $stor } // Define the revision ID schema. - if (!$this->entityType->isRevisionable()) { + if (!$entity_type->isRevisionable()) { $revision_id_schema = $id_schema; $revision_id_schema['description'] = 'The entity revision id this data is attached to, which for an unversioned entity type is the same as the entity id'; } - elseif ($this->fieldStorageDefinitions[$this->entityType->getKey('revision')]->getType() == 'integer') { + elseif ($this->fieldStorageDefinitions[$entity_type->getKey('revision')]->getType() == 'integer') { $revision_id_schema = [ 'type' => 'int', 'unsigned' => TRUE, @@ -2122,7 +2261,6 @@ protected function getDedicatedTableSchema(FieldStorageDefinitionInterface $stor $dedicated_table_schema = [$table_mapping->getDedicatedDataTableName($storage_definition) => $data_schema]; // If the entity type is revisionable, construct the revision table. - $entity_type = $entity_type ?: $this->entityType; if ($entity_type->isRevisionable()) { $revision_schema = $data_schema; $revision_schema['description'] = $description_revision; diff --git a/core/lib/Drupal/Core/EventSubscriber/EntityRouteProviderSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/EntityRouteProviderSubscriber.php index 36a539d736..8246f784dd 100644 --- a/core/lib/Drupal/Core/EventSubscriber/EntityRouteProviderSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/EntityRouteProviderSubscriber.php @@ -2,7 +2,7 @@ namespace Drupal\Core\EventSubscriber; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Routing\RouteBuildEvent; use Drupal\Core\Routing\RoutingEvents; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -14,20 +14,20 @@ class EntityRouteProviderSubscriber implements EventSubscriberInterface { /** - * The entity manager. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * Constructs a new EntityRouteProviderSubscriber instance. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. */ - public function __construct(EntityManagerInterface $entity_manager) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager) { + $this->entityTypeManager = $entity_type_manager; } /** @@ -38,9 +38,9 @@ public function __construct(EntityManagerInterface $entity_manager) { */ public function onDynamicRouteEvent(RouteBuildEvent $event) { $route_collection = $event->getRouteCollection(); - foreach ($this->entityManager->getDefinitions() as $entity_type) { + foreach ($this->entityTypeManager->getDefinitions() as $entity_type) { if ($entity_type->hasRouteProviders()) { - foreach ($this->entityManager->getRouteProviders($entity_type->id()) as $route_provider) { + foreach ($this->entityTypeManager->getRouteProviders($entity_type->id()) as $route_provider) { // Allow to both return an array of routes or a route collection, // like route_callbacks in the routing.yml file. diff --git a/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php b/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php index 8add3b06f9..9f4454860d 100644 --- a/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php +++ b/core/lib/Drupal/Core/Menu/MenuParentFormSelector.php @@ -3,8 +3,8 @@ namespace Drupal\Core\Menu; use Drupal\Core\Cache\CacheableMetadata; -use Drupal\Core\Entity\EntityManagerInterface; use Drupal\Component\Utility\Unicode; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\StringTranslation\StringTranslationTrait; use Drupal\Core\StringTranslation\TranslationInterface; @@ -24,25 +24,25 @@ class MenuParentFormSelector implements MenuParentFormSelectorInterface { protected $menuLinkTree; /** - * The entity manager. + * The entity type manager service. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $entityManager; + protected $entityTypeManager; /** * Constructs a \Drupal\Core\Menu\MenuParentFormSelector * * @param \Drupal\Core\Menu\MenuLinkTreeInterface $menu_link_tree * The menu link tree service. - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager service. * @param \Drupal\Core\StringTranslation\TranslationInterface $string_translation * The string translation service. */ - public function __construct(MenuLinkTreeInterface $menu_link_tree, EntityManagerInterface $entity_manager, TranslationInterface $string_translation) { + public function __construct(MenuLinkTreeInterface $menu_link_tree, EntityTypeManagerInterface $entity_type_manager, TranslationInterface $string_translation) { $this->menuLinkTree = $menu_link_tree; - $this->entityManager = $entity_manager; + $this->entityTypeManager = $entity_type_manager; $this->stringTranslation = $string_translation; } @@ -182,7 +182,7 @@ protected function parentSelectOptionsTreeWalk(array $tree, $menu_name, $indent, * Keys are menu names (ids) values are the menu labels. */ protected function getMenuOptions(array $menu_names = NULL) { - $menus = $this->entityManager->getStorage('menu')->loadMultiple($menu_names); + $menus = $this->entityTypeManager->getStorage('menu')->loadMultiple($menu_names); $options = []; /** @var \Drupal\system\MenuInterface[] $menus */ foreach ($menus as $menu) { diff --git a/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php b/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php index 438eaf8caa..89cfa503cb 100644 --- a/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php +++ b/core/lib/Drupal/Core/ParamConverter/AdminPathConfigEntityConverter.php @@ -3,10 +3,12 @@ namespace Drupal\Core\ParamConverter; use Drupal\Core\Config\Entity\ConfigEntityInterface; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\Routing\AdminContext; use Symfony\Component\Routing\Route; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; /** * Makes sure the unmodified ConfigEntity is loaded on admin pages. @@ -42,15 +44,19 @@ class AdminPathConfigEntityConverter extends EntityConverter { /** * Constructs a new EntityConverter. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * The config factory. * @param \Drupal\Core\Routing\AdminContext $admin_context * The route admin context service. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository + * The entity repository. */ - public function __construct(EntityManagerInterface $entity_manager, ConfigFactoryInterface $config_factory, AdminContext $admin_context) { - parent::__construct($entity_manager); + public function __construct(EntityTypeManagerInterface $entity_type_manager, ConfigFactoryInterface $config_factory, AdminContext $admin_context, LanguageManagerInterface $language_manager = NULL, EntityRepositoryInterface $entity_repository = NULL) { + parent::__construct($entity_type_manager, $language_manager, $entity_repository); $this->configFactory = $config_factory; $this->adminContext = $admin_context; @@ -65,13 +71,13 @@ public function convert($value, $definition, $name, array $defaults) { // If the entity type is dynamic, confirm it to be a config entity. Static // entity types will have performed this check in self::applies(). if (strpos($definition['type'], 'entity:{') === 0) { - $entity_type = $this->entityManager->getDefinition($entity_type_id); + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); if (!$entity_type->entityClassImplements(ConfigEntityInterface::class)) { return parent::convert($value, $definition, $name, $defaults); } } - if ($storage = $this->entityManager->getStorage($entity_type_id)) { + if ($storage = $this->entityTypeManager->getStorage($entity_type_id)) { // Make sure no overrides are loaded. return $storage->loadOverrideFree($value); } @@ -93,7 +99,7 @@ public function applies($definition, $name, Route $route) { } // As we only want to override EntityConverter for ConfigEntities, find // out whether the current entity is a ConfigEntity. - $entity_type = $this->entityManager->getDefinition($entity_type_id); + $entity_type = $this->entityTypeManager->getDefinition($entity_type_id); if ($entity_type->entityClassImplements(ConfigEntityInterface::class)) { return $this->adminContext->isAdminRoute($route); } diff --git a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php index a9f1090857..05dfbcb88e 100644 --- a/core/lib/Drupal/Core/ParamConverter/EntityConverter.php +++ b/core/lib/Drupal/Core/ParamConverter/EntityConverter.php @@ -2,8 +2,11 @@ namespace Drupal\Core\ParamConverter; +use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Entity\RevisionableInterface; use Drupal\Core\Entity\TranslatableRevisionableInterface; use Drupal\Core\Language\LanguageInterface; @@ -64,13 +67,26 @@ * @see entities_revisions_translations */ class EntityConverter implements ParamConverterInterface { + use DeprecatedServicePropertyTrait; /** - * Entity manager which performs the upcasting in the end. + * {@inheritdoc} + */ + protected $deprecatedProperties = ['entityManager' => 'entity.manager']; + + /** + * Entity type manager which performs the upcasting in the end. + * + * @var \Drupal\Core\Entity\EntityTypeManagerInterface + */ + protected $entityTypeManager; + + /** + * Entity repository. * - * @var \Drupal\Core\Entity\EntityManagerInterface + * @var \Drupal\Core\Entity\EntityRepositoryInterface */ - protected $entityManager; + protected $entityRepository; /** * The language manager. @@ -82,13 +98,25 @@ class EntityConverter implements ParamConverterInterface { /** * Constructs a new EntityConverter. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. * @param \Drupal\Core\Language\LanguageManagerInterface|null $language_manager * (optional) The language manager. Defaults to none. + * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository + * The entity repository. */ - public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager = NULL) { - $this->entityManager = $entity_manager; + public function __construct(EntityTypeManagerInterface $entity_type_manager, LanguageManagerInterface $language_manager = NULL, EntityRepositoryInterface $entity_repository = NULL) { + if ($entity_type_manager instanceof EntityManagerInterface) { + @trigger_error('Passing the entity.manager service to EntityConverter::__construct() is deprecated in Drupal 8.7.0 and will be removed before Drupal 9.0.0. Pass the entity_type.manager service instead. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); + } + $this->entityTypeManager = $entity_type_manager; + if ($entity_repository) { + $this->entityRepository = $entity_repository; + } + else { + @trigger_error('The entity.repository service must be passed to EntityConverter::__construct(), it is required before Drupal 9.0.0. See https://www.drupal.org/node/2549139.', E_USER_DEPRECATED); + $this->entityRepository = \Drupal::service('entity.repository'); + } $this->languageManager = $language_manager; } @@ -97,8 +125,8 @@ public function __construct(EntityManagerInterface $entity_manager, LanguageMana */ public function convert($value, $definition, $name, array $defaults) { $entity_type_id = $this->getEntityTypeFromDefaults($definition, $name, $defaults); - $storage = $this->entityManager->getStorage($entity_type_id); - $entity_definition = $this->entityManager->getDefinition($entity_type_id); + $storage = $this->entityTypeManager->getStorage($entity_type_id); + $entity_definition = $this->entityTypeManager->getDefinition($entity_type_id); $entity = $storage->load($value); @@ -115,7 +143,7 @@ public function convert($value, $definition, $name, array $defaults) { // If the entity type is translatable, ensure we return the proper // translation object for the current context. if ($entity instanceof EntityInterface && $entity instanceof TranslatableInterface) { - $entity = $this->entityManager->getTranslationFromContext($entity, NULL, ['operation' => 'entity_upcast']); + $entity = $this->entityRepository->getTranslationFromContext($entity, NULL, ['operation' => 'entity_upcast']); } return $entity; @@ -136,7 +164,7 @@ public function convert($value, $definition, $name, array $defaults) { */ protected function getLatestTranslationAffectedRevision(RevisionableInterface $entity, $langcode) { $revision = NULL; - $storage = $this->entityManager->getStorage($entity->getEntityTypeId()); + $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId()); if ($entity instanceof TranslatableRevisionableInterface && $entity->isTranslatable()) { /** @var \Drupal\Core\Entity\TranslatableRevisionableStorageInterface $storage */ @@ -185,7 +213,7 @@ protected function loadRevision(RevisionableInterface $entity, $revision_id) { // We explicitly perform a loose equality check, since a revision ID may // be returned as an integer or a string. if ($entity->getLoadedRevisionId() != $revision_id) { - $storage = $this->entityManager->getStorage($entity->getEntityTypeId()); + $storage = $this->entityTypeManager->getStorage($entity->getEntityTypeId()); return $storage->loadRevision($revision_id); } else { @@ -203,7 +231,7 @@ public function applies($definition, $name, Route $route) { $entity_type_slug = substr($entity_type_id, 1, -1); return $name != $entity_type_slug && in_array($entity_type_slug, $route->compile()->getVariables(), TRUE); } - return $this->entityManager->hasDefinition($entity_type_id); + return $this->entityTypeManager->hasDefinition($entity_type_id); } return FALSE; } diff --git a/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php b/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php index fb19d18a39..bb884a237d 100644 --- a/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php +++ b/core/modules/views_ui/src/ParamConverter/ViewUIConverter.php @@ -3,7 +3,9 @@ namespace Drupal\views_ui\ParamConverter; use Drupal\Core\Config\ConfigFactoryInterface; -use Drupal\Core\Entity\EntityManagerInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\ParamConverter\AdminPathConfigEntityConverter; use Drupal\Core\Routing\AdminContext; use Symfony\Component\Routing\Route; @@ -39,12 +41,20 @@ class ViewUIConverter extends AdminPathConfigEntityConverter implements ParamCon /** * Constructs a new ViewUIConverter. * - * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager - * The entity manager. + * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager + * The entity type manager. * @param \Drupal\Core\TempStore\SharedTempStoreFactory $temp_store_factory * The factory for the temp store object. + * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory + * The config factory. + * @param \Drupal\Core\Routing\AdminContext $admin_context + * The route admin context service. + * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager + * The language manager. + * @param \Drupal\Core\Entity\EntityRepositoryInterface $entity_repository + * The entity repository. */ - public function __construct(EntityManagerInterface $entity_manager, SharedTempStoreFactory $temp_store_factory, ConfigFactoryInterface $config_factory = NULL, AdminContext $admin_context = NULL) { + public function __construct(EntityTypeManagerInterface $entity_type_manager, SharedTempStoreFactory $temp_store_factory, ConfigFactoryInterface $config_factory = NULL, AdminContext $admin_context = NULL, LanguageManagerInterface $language_manager = NULL, EntityRepositoryInterface $entity_repository = NULL) { // The config factory and admin context are new arguments due to changing // the parent. Avoid an error on updated sites by falling back to getting // them from the container. @@ -55,7 +65,7 @@ public function __construct(EntityManagerInterface $entity_manager, SharedTempSt if (!$admin_context) { $admin_context = \Drupal::service('router.admin_context'); } - parent::__construct($entity_manager, $config_factory, $admin_context); + parent::__construct($entity_type_manager, $config_factory, $admin_context, $language_manager, $entity_repository); $this->tempStoreFactory = $temp_store_factory; } diff --git a/core/modules/views_ui/views_ui.services.yml b/core/modules/views_ui/views_ui.services.yml index adf87e61a2..33c9e181ed 100644 --- a/core/modules/views_ui/views_ui.services.yml +++ b/core/modules/views_ui/views_ui.services.yml @@ -1,7 +1,7 @@ services: paramconverter.views_ui: class: Drupal\views_ui\ParamConverter\ViewUIConverter - arguments: ['@entity.manager', '@tempstore.shared', '@config.factory', '@router.admin_context'] + arguments: ['@entity_type.manager', '@tempstore.shared', '@config.factory', '@router.admin_context', '@language_manager', '@entity.repository'] tags: - { name: paramconverter, priority: 10 } lazy: true diff --git a/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php b/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php index 39a5accae3..8d3a1c2c41 100644 --- a/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php +++ b/core/tests/Drupal/KernelTests/Config/DefaultConfigTest.php @@ -126,7 +126,7 @@ protected function doTestsOnConfigStorage(StorageInterface $default_config_stora // recalculation of dependencies does not cause config change. if ($entity_type = $config_manager->getEntityTypeIdByName($config_name)) { $entity_storage = $config_manager - ->getEntityManager() + ->getEntityTypeManager() ->getStorage($entity_type); $id = $entity_storage->getIDFromConfigName($config_name, $entity_storage->getEntityType() ->getConfigPrefix()); diff --git a/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionSchemaUpdateTest.php b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionSchemaUpdateTest.php new file mode 100644 index 0000000000..84f5c6718d --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Entity/EntityDefinitionSchemaUpdateTest.php @@ -0,0 +1,540 @@ +entityDefinitionUpdateManager = $this->container->get('entity.definition_update_manager'); + $this->entityTypeManager = $this->container->get('entity_type.manager'); + $this->entityFieldManager = $this->container->get('entity_field.manager'); + $this->database = $this->container->get('database'); + + $this->installEntitySchema($this->entityTypeId); + $this->installEntitySchema('configurable_language'); + + // Enable an additional language. + ConfigurableLanguage::createFromLangcode('ro')->save(); + } + + /** + * @covers ::updateEntityTypeSchema + * @dataProvider providerTestEntityTypeSchemaUpdates + */ + public function testEntityTypeSchemaUpdates($initial_rev, $initial_mul, $new_rev, $new_mul, $data_migration_supported) { + // The 'entity_test_update' entity type is neither revisionable nor + // translatable by default, so we need to get it into the initial testing + // state. This also covers the "no existing data" scenario for entity schema + // updates. + if ($initial_rev || $initial_mul) { + $entity_type = $this->updateEntityTypeDefinition($initial_rev, $initial_mul); + $field_storage_definitions = $this->updateFieldStorageDefinitions($initial_rev, $initial_mul); + + $this->entityDefinitionUpdateManager->updateEntityTypeSchema($entity_type, $field_storage_definitions); + $this->assertEntityTypeSchema($initial_rev, $initial_mul); + } + + // Add a few entities so we can test the data copying step. + $this->insertInitialData($initial_rev, $initial_mul); + + $updated_entity_type = $this->updateEntityTypeDefinition($new_rev, $new_mul); + $updated_field_storage_definitions = $this->updateFieldStorageDefinitions($new_rev, $new_mul); + + if (!$data_migration_supported) { + $this->setExpectedException(EntityStorageException::class, 'Converting an entity type from revisionable to non-revisionable or from translatable to non-translatable is not supported.'); + } + + $sandbox = []; + $this->entityDefinitionUpdateManager->updateEntityTypeSchema($updated_entity_type, $updated_field_storage_definitions, $sandbox); + $this->assertEntityTypeSchema($new_rev, $new_mul); + $this->assertEntityData($initial_rev, $initial_mul); + } + + /** + * Data provider for testEntityTypeSchemaUpdates(). + */ + public function providerTestEntityTypeSchemaUpdates() { + return [ + 'no change' => [ + 'initial_rev' => FALSE, + 'initial_mul' => FALSE, + 'new_rev' => FALSE, + 'new_mul' => FALSE, + 'data_migration_supported' => TRUE, + ], + 'non_rev non_mul to rev non_mul' => [ + 'initial_rev' => FALSE, + 'initial_mul' => FALSE, + 'new_rev' => TRUE, + 'new_mul' => FALSE, + 'data_migration_supported' => TRUE, + ], + 'non_rev non_mul to rev mul' => [ + 'initial_rev' => FALSE, + 'initial_mul' => FALSE, + 'new_rev' => TRUE, + 'new_mul' => TRUE, + 'data_migration_supported' => TRUE, + ], + 'non_rev non_mul to non_rev mul' => [ + 'initial_rev' => FALSE, + 'initial_mul' => FALSE, + 'new_rev' => FALSE, + 'new_mul' => TRUE, + 'data_migration_supported' => TRUE, + ], + 'rev non_mul to non_rev non_mul' => [ + 'initial_rev' => TRUE, + 'initial_mul' => FALSE, + 'new_rev' => FALSE, + 'new_mul' => FALSE, + 'data_migration_supported' => FALSE, + ], + 'rev non_mul to non_rev mul' => [ + 'initial_rev' => TRUE, + 'initial_mul' => FALSE, + 'new_rev' => FALSE, + 'new_mul' => TRUE, + 'data_migration_supported' => FALSE, + ], + // @todo Entity schema updates with data migration for revisionable entity + // types is not supported yet. + // 'rev non_mul to rev mul' => [], + 'non_rev mul to non_rev non_mul' => [ + 'initial_rev' => FALSE, + 'initial_mul' => TRUE, + 'new_rev' => FALSE, + 'new_mul' => FALSE, + 'data_migration_supported' => FALSE, + ], + 'non_rev mul to rev non_mul' => [ + 'initial_rev' => FALSE, + 'initial_mul' => TRUE, + 'new_rev' => TRUE, + 'new_mul' => FALSE, + 'data_migration_supported' => FALSE, + ], + 'non_rev mul to rev mul' => [ + 'initial_rev' => FALSE, + 'initial_mul' => TRUE, + 'new_rev' => TRUE, + 'new_mul' => TRUE, + 'data_migration_supported' => TRUE, + ], + 'rev mul to non_rev non_mul' => [ + 'initial_rev' => TRUE, + 'initial_mul' => TRUE, + 'new_rev' => FALSE, + 'new_mul' => FALSE, + 'data_migration_supported' => FALSE, + ], + 'rev mul to rev non_mul' => [ + 'initial_rev' => TRUE, + 'initial_mul' => TRUE, + 'new_rev' => TRUE, + 'new_mul' => FALSE, + 'data_migration_supported' => FALSE, + ], + 'rev mul to non_rev mul' => [ + 'initial_rev' => TRUE, + 'initial_mul' => TRUE, + 'new_rev' => FALSE, + 'new_mul' => TRUE, + 'data_migration_supported' => FALSE, + ], + ]; + } + + /** + * Generates test entities for the 'entity_test_update' entity type. + * + * @param bool $revisionable + * Whether the entity type is revisionable or not. + * @param bool $translatable + * Whether the entity type is translatable or not. + */ + protected function insertInitialData($revisionable, $translatable) { + // Add three test entities in order to make the "data copy" step run at + // least three times. + /** @var \Drupal\Core\Entity\TranslatableRevisionableStorageInterface|\Drupal\Core\Entity\EntityStorageInterface $storage */ + $storage = $this->entityTypeManager->getStorage($this->entityTypeId); + + // Create test entities with two translations and two revisions. + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + for ($i = 1; $i <= 3; $i++) { + $entity = $storage->create([ + 'id' => $i, + 'name' => 'test entity - ' . $i . ' - en', + ]); + $entity->save(); + + if ($translatable) { + $translation = $entity->addTranslation('ro', [ + 'name' => 'test entity - ' . $i . ' - ro', + ]); + $translation->save(); + } + + if ($revisionable) { + // Create a new pending revision. + $revision_2 = $storage->createRevision($entity, FALSE); + $revision_2->name = 'test entity - ' . $i . ' - en - rev2'; + $revision_2->save(); + + if ($translatable) { + $translation = $revision_2->getTranslation('ro'); + $translation->name = 'test entity - ' . $i . ' - ro - rev2'; + $translation->save(); + } + } + } + } + + /** + * Asserts test entity data after a entity type schema update. + * + * @param bool $revisionable + * Whether the entity type was revisionable prior to the update. + * @param bool $translatable + * Whether the entity type was translatable prior to the update. + */ + protected function assertEntityData($revisionable, $translatable) { + $entities = $this->entityTypeManager->getStorage($this->entityTypeId)->loadMultiple(); + $this->assertCount(3, $entities); + foreach ($entities as $entity_id => $entity) { + /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */ + $this->assertEquals("test entity - {$entity->id()} - en", $entity->label()); + + if ($translatable) { + $translation = $entity->getTranslation('ro'); + $this->assertEquals("test entity - {$entity->id()} - ro", $translation->label()); + } + } + + if ($revisionable) { + $revisions = $this->entityTypeManager->getStorage($this->entityTypeId)->loadMultipleRevisions(); + $this->assertCount(6, $revisions); + + foreach ($revisions as $revision) { + /** @var \Drupal\Core\Entity\ContentEntityInterface $revision */ + $revision_label = $revision->isDefaultRevision() ?: ' - rev2'; + $this->assertEquals("test entity - {$revision->id()} - en{$revision_label}", $revision->label()); + + if ($translatable) { + $translation = $revision->getTranslation('ro'); + $this->assertEquals("test entity - {$translation->id()} - ro{$revision_label}", $translation->label()); + } + } + } + } + + /** + * Returns an entity type definition, possibly updated to be rev or mul. + * + * @param bool $revisionable + * (optional) Whether the entity type should be revisionable or not. + * Defaults toFALSE. + * @param bool $translatable + * (optional) Whether the entity type should be translatable or not. + * Defaults to FALSE. + * + * @return \Drupal\Core\Entity\EntityTypeInterface + * An entity type definition. + */ + protected function updateEntityTypeDefinition($revisionable = FALSE, $translatable = FALSE) { + $entity_type = clone $this->entityTypeManager->getDefinition($this->entityTypeId); + + if ($revisionable) { + $keys = $entity_type->getKeys(); + $keys['revision'] = 'revision_id'; + $entity_type->set('entity_keys', $keys); + $entity_type->set('revision_table', 'entity_test_update_revision'); + } + else { + $keys = $entity_type->getKeys(); + $keys['revision'] = ''; + $entity_type->set('entity_keys', $keys); + $entity_type->set('revision_table', NULL); + } + + if ($translatable) { + $entity_type->set('translatable', TRUE); + $entity_type->set('data_table', 'entity_test_update_data'); + } + else { + $entity_type->set('translatable', FALSE); + $entity_type->set('data_table', NULL); + } + + if ($revisionable && $translatable) { + $entity_type->set('revision_data_table', 'entity_test_update_revision_data'); + } + else { + $entity_type->set('revision_data_table', NULL); + } + + $this->state->set('entity_test_update.entity_type', $entity_type); + $this->entityTypeManager->clearCachedDefinitions(); + $this->entityFieldManager->clearCachedFieldDefinitions(); + + return $entity_type; + } + + /** + * Returns the required rev / mul field definitions for an entity type. + * + * @param bool $revisionable + * (optional) Whether the entity type should be revisionable or not. + * Defaults toFALSE. + * @param bool $translatable + * (optional) Whether the entity type should be translatable or not. + * Defaults to FALSE. + * + * @return \Drupal\Core\Field\FieldStorageDefinitionInterface[] + * An array of field storage definition objects. + */ + protected function updateFieldStorageDefinitions($revisionable = FALSE, $translatable = FALSE) { + $field_storage_definitions = $this->entityFieldManager->getFieldStorageDefinitions($this->entityTypeId); + + if ($revisionable) { + // The 'langcode' is already available for the 'entity_test_update' entity + // type because it has the 'langcode' entity key defined. + $field_storage_definitions['langcode']->setRevisionable(TRUE); + + $field_storage_definitions['revision_id'] = BaseFieldDefinition::create('integer') + ->setName('revision_id') + ->setTargetEntityTypeId($this->entityTypeId) + ->setTargetBundle(NULL) + ->setLabel(new TranslatableMarkup('Revision ID')) + ->setReadOnly(TRUE) + ->setSetting('unsigned', TRUE); + + $field_storage_definitions['revision_default'] = BaseFieldDefinition::create('boolean') + ->setName('revision_default') + ->setTargetEntityTypeId($this->entityTypeId) + ->setTargetBundle(NULL) + ->setLabel(new TranslatableMarkup('Default revision')) + ->setDescription(new TranslatableMarkup('A flag indicating whether this was a default revision when it was saved.')) + ->setStorageRequired(TRUE) + ->setInternal(TRUE) + ->setTranslatable(FALSE) + ->setRevisionable(TRUE); + } + + if ($translatable) { + // The 'langcode' is already available for the 'entity_test_update' entity + // type because it has the 'langcode' entity key defined. + $field_storage_definitions['langcode']->setTranslatable(TRUE); + + $field_storage_definitions['default_langcode'] = BaseFieldDefinition::create('boolean') + ->setName('default_langcode') + ->setTargetEntityTypeId($this->entityTypeId) + ->setTargetBundle(NULL) + ->setLabel(new TranslatableMarkup('Default translation')) + ->setDescription(new TranslatableMarkup('A flag indicating whether this is the default translation.')) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE) + ->setDefaultValue(TRUE); + } + + if ($revisionable && $translatable) { + $field_storage_definitions['revision_translation_affected'] = BaseFieldDefinition::create('boolean') + ->setName('revision_translation_affected') + ->setTargetEntityTypeId($this->entityTypeId) + ->setTargetBundle(NULL) + ->setLabel(new TranslatableMarkup('Revision translation affected')) + ->setDescription(new TranslatableMarkup('Indicates if the last edit of a translation belongs to current revision.')) + ->setReadOnly(TRUE) + ->setRevisionable(TRUE) + ->setTranslatable(TRUE); + } + + return $field_storage_definitions; + } + + /** + * Asserts revisionable and/or translatable characteristics of an entity type. + * + * @param bool $revisionable + * Whether the entity type is revisionable or not. + * @param bool $translatable + * Whether the entity type is translatable or not. + */ + protected function assertEntityTypeSchema($revisionable, $translatable) { + if ($revisionable && $translatable) { + $this->assertRevisionableAndTranslatable(); + } + elseif ($revisionable) { + $this->assertRevisionable(); + } + elseif ($translatable) { + $this->assertTranslatable(); + } + } + + /** + * Asserts the revisionable characteristics of an entity type. + */ + protected function assertRevisionable() { + /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ + $entity_type = $this->entityDefinitionUpdateManager->getEntityType($this->entityTypeId); + $this->assertTrue($entity_type->isRevisionable()); + + // Check that the required field definitions of a revisionable entity type + // exists and are stored in the correct tables. + $revision_key = $entity_type->getKey('revision'); + $revision_default_key = $entity_type->getRevisionMetadataKey('revision_default'); + $revision_field = $this->entityDefinitionUpdateManager->getFieldStorageDefinition($revision_key, $entity_type->id()); + $revision_default_field = $this->entityDefinitionUpdateManager->getFieldStorageDefinition($revision_default_key, $entity_type->id()); + $this->assertNotNull($revision_field); + $this->assertNotNull($revision_default_field); + + $database_schema = $this->database->schema(); + $base_table = $entity_type->getBaseTable(); + $revision_table = $entity_type->getRevisionTable(); + $this->assertTrue($database_schema->tableExists($revision_table)); + + $this->assertTrue($database_schema->fieldExists($base_table, $revision_key)); + $this->assertTrue($database_schema->fieldExists($revision_table, $revision_key)); + + $this->assertFalse($database_schema->fieldExists($base_table, $revision_default_key)); + $this->assertTrue($database_schema->fieldExists($revision_table, $revision_default_key)); + } + + /** + * Asserts the translatable characteristics of an entity type. + */ + protected function assertTranslatable() { + /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ + $entity_type = $this->entityDefinitionUpdateManager->getEntityType($this->entityTypeId); + $this->assertTrue($entity_type->isTranslatable()); + + // Check that the required field definitions of a translatable entity type + // exists and are stored in the correct tables. + $langcode_key = $entity_type->getKey('langcode'); + $default_langcode_key = $entity_type->getKey('default_langcode'); + $langcode_field = $this->entityDefinitionUpdateManager->getFieldStorageDefinition($langcode_key, $entity_type->id()); + $default_langcode_field = $this->entityDefinitionUpdateManager->getFieldStorageDefinition($default_langcode_key, $entity_type->id()); + $this->assertNotNull($langcode_field); + $this->assertNotNull($default_langcode_field); + + $database_schema = $this->database->schema(); + $base_table = $entity_type->getBaseTable(); + $data_table = $entity_type->getDataTable(); + $this->assertTrue($database_schema->tableExists($data_table)); + + $this->assertTrue($database_schema->fieldExists($base_table, $langcode_key)); + $this->assertTrue($database_schema->fieldExists($data_table, $langcode_key)); + + $this->assertFalse($database_schema->fieldExists($base_table, $default_langcode_key)); + $this->assertTrue($database_schema->fieldExists($data_table, $default_langcode_key)); + } + + /** + * Asserts the revisionable / translatable characteristics of an entity type. + */ + protected function assertRevisionableAndTranslatable() { + $this->assertRevisionable(); + $this->assertTranslatable(); + + // Check that the required field definitions of a revisionable and + // translatable entity type exists and are stored in the correct tables. + /** @var \Drupal\Core\Entity\ContentEntityTypeInterface $entity_type */ + $entity_type = $this->entityDefinitionUpdateManager->getEntityType($this->entityTypeId); + $langcode_key = $entity_type->getKey('langcode'); + $revision_translation_affected_key = $entity_type->getKey('revision_translation_affected'); + $revision_translation_affected_field = $this->entityDefinitionUpdateManager->getFieldStorageDefinition($revision_translation_affected_key, $entity_type->id()); + $this->assertNotNull($revision_translation_affected_field); + + $database_schema = $this->database->schema(); + $base_table = $entity_type->getBaseTable(); + $data_table = $entity_type->getDataTable(); + $revision_table = $entity_type->getRevisionTable(); + $revision_data_table = $entity_type->getRevisionDataTable(); + $this->assertTrue($database_schema->tableExists($revision_data_table)); + + $this->assertTrue($database_schema->fieldExists($base_table, $langcode_key)); + $this->assertTrue($database_schema->fieldExists($data_table, $langcode_key)); + $this->assertTrue($database_schema->fieldExists($revision_table, $langcode_key)); + $this->assertTrue($database_schema->fieldExists($revision_data_table, $langcode_key)); + + $this->assertFalse($database_schema->fieldExists($base_table, $revision_translation_affected_key)); + $this->assertFalse($database_schema->fieldExists($revision_table, $revision_translation_affected_key)); + $this->assertTrue($database_schema->fieldExists($data_table, $revision_translation_affected_key)); + $this->assertTrue($database_schema->fieldExists($revision_data_table, $revision_translation_affected_key)); + + // Also check the revision metadata keys, if they exist. + foreach (['revision_log_message', 'revision_user', 'revision_created'] as $key) { + if ($revision_metadata_key = $entity_type->getRevisionMetadataKey($key)) { + $revision_metadata_field = $this->entityDefinitionUpdateManager->getFieldStorageDefinition($revision_metadata_key, $entity_type->id()); + $this->assertNotNull($revision_metadata_field); + $this->assertFalse($database_schema->fieldExists($base_table, $revision_metadata_key)); + $this->assertTrue($database_schema->fieldExists($revision_table, $revision_metadata_key)); + $this->assertFalse($database_schema->fieldExists($data_table, $revision_metadata_key)); + $this->assertFalse($database_schema->fieldExists($revision_data_table, $revision_metadata_key)); + } + } + } + +} diff --git a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php index 7dd8e193cb..3b73b67a10 100644 --- a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php +++ b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php @@ -19,9 +19,9 @@ class DateTest extends UnitTestCase { /** * The mocked entity manager. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The mocked language manager. @@ -63,8 +63,8 @@ protected function setUp() { $entity_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface'); - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $this->entityManager->expects($this->any())->method('getStorage')->with('date_format')->willReturn($entity_storage); + $this->entityTypeManager = $this->getMock('Drupal\Core\Entity\EntityTypeManagerInterface'); + $this->entityTypeManager->expects($this->any())->method('getStorage')->with('date_format')->willReturn($entity_storage); $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface'); $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface'); @@ -76,10 +76,10 @@ protected function setUp() { $container->set('string_translation', $this->getStringTranslationStub()); \Drupal::setContainer($container); - $this->dateFormatter = new DateFormatter($this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub(), $this->requestStack); + $this->dateFormatter = new DateFormatter($this->entityTypeManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub(), $this->requestStack); $this->dateFormatterStub = $this->getMockBuilder('\Drupal\Core\Datetime\DateFormatter') - ->setConstructorArgs([$this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub(), $this->requestStack]) + ->setConstructorArgs([$this->entityTypeManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub(), $this->requestStack]) ->setMethods(['formatDiff']) ->getMock(); } diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityCreateAccessCheckTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityCreateAccessCheckTest.php index 59f83af029..8f648ac07b 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityCreateAccessCheckTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityCreateAccessCheckTest.php @@ -20,9 +20,9 @@ class EntityCreateAccessCheckTest extends UnitTestCase { /** * The mocked entity manager. * - * @var \PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - public $entityManager; + public $entityTypeManager; /** * {@inheritdoc} @@ -36,6 +36,8 @@ protected function setUp() { $container = new Container(); $container->set('cache_contexts_manager', $cache_contexts_manager); \Drupal::setContainer($container); + + $this->entityTypeManager = $this->getMock('Drupal\Core\Entity\EntityTypeManagerInterface'); } /** @@ -78,8 +80,6 @@ public function testAccess($entity_bundle, $requirement, $access, $expected, $ex $expected_access_result->setReason("Could not find '{bundle_argument}' request argument, therefore cannot check create access."); } - $entity_manager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - // Don't expect a call to the access control handler when we have a bundle // argument requirement but no bundle is provided. if ($entity_bundle || strpos($requirement, '{') === FALSE) { @@ -89,12 +89,12 @@ public function testAccess($entity_bundle, $requirement, $access, $expected, $ex ->with($entity_bundle) ->will($this->returnValue($access_result)); - $entity_manager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getAccessControlHandler') ->will($this->returnValue($access_control_handler)); } - $applies_check = new EntityCreateAccessCheck($entity_manager); + $applies_check = new EntityCreateAccessCheck($this->entityTypeManager); $route = $this->getMockBuilder('Symfony\Component\Routing\Route') ->disableOriginalConstructor() diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityFormBuilderTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityFormBuilderTest.php index 963c1a1dc3..6d7df9fdf2 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityFormBuilderTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityFormBuilderTest.php @@ -14,9 +14,9 @@ class EntityFormBuilderTest extends UnitTestCase { /** * The entity manager. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The form builder. @@ -39,8 +39,8 @@ protected function setUp() { parent::setUp(); $this->formBuilder = $this->getMock('Drupal\Core\Form\FormBuilderInterface'); - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); - $this->entityFormBuilder = new EntityFormBuilder($this->entityManager, $this->formBuilder); + $this->entityTypeManager = $this->getMock('Drupal\Core\Entity\EntityTypeManagerInterface'); + $this->entityFormBuilder = new EntityFormBuilder($this->entityTypeManager, $this->formBuilder); } /** @@ -53,7 +53,7 @@ public function testGetForm() { $form_controller->expects($this->any()) ->method('getFormId') ->will($this->returnValue('the_form_id')); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getFormObject') ->with('the_entity_type', 'default') ->will($this->returnValue($form_controller)); diff --git a/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php b/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php index cdd41e6d8c..4896487e19 100644 --- a/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/EntityResolverManagerTest.php @@ -32,9 +32,9 @@ class EntityResolverManagerTest extends UnitTestCase { /** * The mocked entity manager. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; /** * The mocked class resolver. @@ -56,11 +56,11 @@ class EntityResolverManagerTest extends UnitTestCase { * @covers ::__construct */ protected function setUp() { - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); + $this->entityTypeManager = $this->getMock('Drupal\Core\Entity\EntityTypeManagerInterface'); $this->container = $this->getMock('Symfony\Component\DependencyInjection\ContainerInterface'); $this->classResolver = $this->getClassResolverStub(); - $this->entityResolverManager = new EntityResolverManager($this->entityManager, $this->classResolver); + $this->entityResolverManager = new EntityResolverManager($this->entityTypeManager, $this->classResolver); } /** @@ -455,13 +455,13 @@ protected function setupEntityTypes() { $revisionable_definition->expects($this->any()) ->method('isRevisionable') ->willReturn(TRUE); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinitions') ->will($this->returnValue([ 'entity_test' => $definition, 'entity_test_rev' => $revisionable_definition, ])); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->will($this->returnCallback(function ($entity_type) use ($definition, $revisionable_definition) { if ($entity_type == 'entity_test') { diff --git a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php index cf18e2bb9c..c744e663f0 100644 --- a/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php +++ b/core/tests/Drupal/Tests/Core/Entity/Sql/SqlContentEntityStorageSchemaTest.php @@ -409,7 +409,7 @@ public function testGetSchemaRevisionable() { ->method('getRevisionMetadataKeys') ->will($this->returnValue([])); - $this->storage->expects($this->exactly(2)) + $this->storage->expects($this->exactly(1)) ->method('getRevisionTable') ->will($this->returnValue('entity_test_revision')); @@ -505,6 +505,7 @@ public function testGetSchemaTranslatable() { 'id' => 'id', 'langcode' => 'langcode', ], + 'translatable' => TRUE, ]); $this->storage->expects($this->any()) @@ -622,24 +623,22 @@ public function testGetSchemaRevisionableTranslatable() { 'revision' => 'revision_id', 'langcode' => 'langcode', ], + 'revision_data_table' => 'entity_test_revision_field_data', ], ]) - ->setMethods(['getRevisionMetadataKeys']) + ->setMethods(['isRevisionable', 'isTranslatable', 'getRevisionMetadataKeys']) ->getMock(); $this->entityType->expects($this->any()) - ->method('getRevisionMetadataKeys') - ->will($this->returnValue([])); + ->method('isRevisionable') + ->will($this->returnValue(TRUE)); + $this->entityType->expects($this->any()) + ->method('isTranslatable') + ->will($this->returnValue(TRUE)); - $this->storage->expects($this->exactly(3)) + $this->storage->expects($this->exactly(2)) ->method('getRevisionTable') ->will($this->returnValue('entity_test_revision')); - $this->storage->expects($this->once()) - ->method('getDataTable') - ->will($this->returnValue('entity_test_field_data')); - $this->storage->expects($this->once()) - ->method('getRevisionDataTable') - ->will($this->returnValue('entity_test_revision_field_data')); $this->setUpStorageDefinition('revision_id', [ 'columns' => [ diff --git a/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php b/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php index b48b9b73a9..6ba892e56d 100644 --- a/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php +++ b/core/tests/Drupal/Tests/Core/ParamConverter/EntityConverterTest.php @@ -6,6 +6,8 @@ use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\ContentEntityStorageInterface; use Drupal\Core\Entity\ContentEntityTypeInterface; +use Drupal\Core\Entity\EntityRepositoryInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\Core\ParamConverter\EntityConverter; @@ -22,11 +24,25 @@ class EntityConverterTest extends UnitTestCase { /** - * The mocked entity manager. + * The mocked entity type manager. * - * @var \Drupal\Core\Entity\EntityManagerInterface|\PHPUnit_Framework_MockObject_MockObject + * @var \Drupal\Core\Entity\EntityTypeManagerInterface|\PHPUnit_Framework_MockObject_MockObject */ - protected $entityManager; + protected $entityTypeManager; + + /** + * The mocked language manager. + * + * @var \Drupal\Core\Language\LanguageManagerInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $languageManager; + + /** + * The mocked entities repository. + * + * @var \Drupal\Core\Entity\EntityRepositoryInterface|\PHPUnit_Framework_MockObject_MockObject + */ + protected $entityRepository; /** * The tested entity converter. @@ -41,9 +57,11 @@ class EntityConverterTest extends UnitTestCase { protected function setUp() { parent::setUp(); - $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface'); + $this->entityTypeManager = $this->createMock(EntityTypeManagerInterface::class); + $this->languageManager = $this->createMock(LanguageManagerInterface::class); + $this->entityRepository = $this->createMock(EntityRepositoryInterface::class); - $this->entityConverter = new EntityConverter($this->entityManager); + $this->entityConverter = new EntityConverter($this->entityTypeManager, $this->languageManager, $this->entityRepository); } /** @@ -54,7 +72,7 @@ protected function setUp() { * @covers ::applies */ public function testApplies(array $definition, $name, Route $route, $applies) { - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('hasDefinition') ->willReturnCallback(function ($entity_type) { return 'entity_test' == $entity_type; @@ -86,7 +104,7 @@ public function providerTestApplies() { */ public function testConvert($value, array $definition, array $defaults, $expected_result) { $entity_storage = $this->getMock('Drupal\Core\Entity\EntityStorageInterface'); - $this->entityManager->expects($this->once()) + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with('entity_test') ->willReturn($entity_storage); @@ -119,7 +137,7 @@ public function providerTestConvert() { * Tests the convert() method with an invalid entity type. */ public function testConvertWithInvalidEntityType() { - $this->entityManager->expects($this->once()) + $this->entityTypeManager->expects($this->once()) ->method('getStorage') ->with('invalid_id') ->willThrowException(new InvalidPluginDefinitionException('invalid_id')); @@ -168,7 +186,7 @@ public function testDeprecatedOptionalLanguageManager() { ->with('id') ->willReturn('revision_id'); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getStorage') ->with('entity_test') ->willReturn($storage); @@ -178,7 +196,7 @@ public function testDeprecatedOptionalLanguageManager() { ->method('isRevisionable') ->willReturn(TRUE); - $this->entityManager->expects($this->any()) + $this->entityTypeManager->expects($this->any()) ->method('getDefinition') ->with('entity_test') ->willReturn($entity_type); @@ -203,6 +221,8 @@ public function testDeprecatedOptionalLanguageManager() { \Drupal::setContainer($container); $definition = ['type' => 'entity:entity_test', 'load_latest_revision' => TRUE]; + + $this->entityConverter = new EntityConverter($this->entityTypeManager, NULL, $this->entityRepository); $this->entityConverter->convert('id', $definition, 'foo', []); }