From bdf48170cdd9d743715de690b8a08a33065b2bcb Mon Sep 17 00:00:00 2001 From: catch Date: Thu, 24 Oct 2019 09:35:41 +0100 Subject: [PATCH] Issue #2233595 by amateescu, marcingy, SpartyDan, visabhishek, Sharique, carletex, slashrsm, hanoii, plach, Berdir, xjm: Deprecate the custom path alias storage --- core/core.services.yml | 10 +- core/lib/Drupal/Core/Path/AliasManager.php | 42 ++-- .../Core/Path/AliasManagerInterface.php | 2 - core/lib/Drupal/Core/Path/AliasRepository.php | 142 +++++++++++ .../Core/Path/AliasRepositoryInterface.php | 81 ++++++ core/lib/Drupal/Core/Path/AliasStorage.php | 9 + .../Core/Path/AliasStorageInterface.php | 7 + core/lib/Drupal/Core/Path/AliasWhitelist.php | 29 ++- .../tests/src/Functional/LinkFieldTest.php | 5 +- .../tests/src/Functional/LocalePathTest.php | 32 +-- .../Kernel/PathAliasMenuLinkContentTest.php | 17 +- .../Field/FieldType/PathFieldItemList.php | 32 +-- .../Plugin/Field/FieldWidget/PathWidget.php | 23 +- .../Plugin/migrate/source/UrlAliasBase.php | 2 +- .../tests/src/Functional/PathAliasTest.php | 8 +- .../tests/src/Functional/PathLanguageTest.php | 4 +- .../tests/src/Functional/PathTestBase.php | 3 + .../Kernel/Migrate/d6/MigrateUrlAliasTest.php | 81 +++--- .../Kernel/Migrate/d7/MigrateUrlAliasTest.php | 26 +- .../Migrate/d7/MigrateUrlAliasTestBase.php | 21 +- .../path/tests/src/Kernel/PathItemTest.php | 45 ++-- .../src/Functional/ShortcutLinksTest.php | 10 +- .../Path/UrlAlterFunctionalTest.php | 8 +- .../system/tests/src/Kernel/PathHooksTest.php | 39 +-- ...rage.php => WorkspacesAliasRepository.php} | 21 +- .../src/WorkspacesServiceProvider.php | 8 +- .../Installer/DistributionProfileTest.php | 2 +- .../Routing/PathEncodedTest.php | 8 +- ...eCachingNonPathLanguageNegotiationTest.php | 5 +- .../KernelTests/Core/Command/DbDumpTest.php | 5 +- .../Core/Path/AliasStorageTest.php | 83 ------ .../KernelTests/Core/Path/AliasTest.php | 207 +++++---------- .../Core/Path/LegacyAliasStorageTest.php | 238 ++++++++++++++++++ .../Routing/ContentNegotiationRoutingTest.php | 9 +- .../Core/Routing/RouteProviderTest.php | 7 +- .../Tests/Core/Path/AliasManagerTest.php | 89 ++++--- .../Tests/Traits/Core/PathAliasTestTrait.php | 112 +++++++++ 37 files changed, 965 insertions(+), 507 deletions(-) create mode 100644 core/lib/Drupal/Core/Path/AliasRepository.php create mode 100644 core/lib/Drupal/Core/Path/AliasRepositoryInterface.php rename core/modules/workspaces/src/{AliasStorage.php => WorkspacesAliasRepository.php} (58%) delete mode 100644 core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php create mode 100644 core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php create mode 100644 core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php diff --git a/core/core.services.yml b/core/core.services.yml index fee9101701c..9d6ad2711a1 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -465,10 +465,10 @@ services: class: Drupal\Core\Path\AliasWhitelist tags: - { name: needs_destruction } - arguments: [path_alias_whitelist, '@cache.bootstrap', '@lock', '@state', '@path.alias_storage'] + arguments: [path_alias_whitelist, '@cache.bootstrap', '@lock', '@state', '@path.alias_repository'] path.alias_manager: class: Drupal\Core\Path\AliasManager - arguments: ['@path.alias_storage', '@path.alias_whitelist', '@language_manager', '@cache.data'] + arguments: ['@path.alias_repository', '@path.alias_whitelist', '@language_manager', '@cache.data'] path.current: class: Drupal\Core\Path\CurrentPathStack arguments: ['@request_stack'] @@ -960,11 +960,17 @@ services: arguments: ['@lock', '@plugin.manager.menu.link', '@database', '@database.replica_kill_switch'] tags: - { name: event_subscriber } + path.alias_repository: + class: Drupal\Core\Path\AliasRepository + arguments: ['@database'] + tags: + - { name: backend_overridable } path.alias_storage: class: Drupal\Core\Path\AliasStorage arguments: ['@database', '@module_handler', '@entity_type.manager'] tags: - { name: backend_overridable } + deprecated: The "%service_id%" service is deprecated. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865 path.matcher: class: Drupal\Core\Path\PathMatcher arguments: ['@config.factory', '@current_route_match'] diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php index 39472797ad6..59694bb2f85 100644 --- a/core/lib/Drupal/Core/Path/AliasManager.php +++ b/core/lib/Drupal/Core/Path/AliasManager.php @@ -4,6 +4,7 @@ namespace Drupal\Core\Path; use Drupal\Core\Cache\CacheBackendInterface; use Drupal\Core\CacheDecorator\CacheDecoratorInterface; +use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Language\LanguageManagerInterface; @@ -12,12 +13,19 @@ use Drupal\Core\Language\LanguageManagerInterface; */ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { + use DeprecatedServicePropertyTrait; + /** - * The alias storage service. - * - * @var \Drupal\Core\Path\AliasStorageInterface + * {@inheritdoc} */ - protected $storage; + protected $deprecatedProperties = ['storage' => 'path.alias_storage']; + + /** + * The path alias repository. + * + * @var \Drupal\Core\Path\AliasRepositoryInterface + */ + protected $pathAliasRepository; /** * Cache backend service. @@ -95,8 +103,8 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { /** * Constructs an AliasManager. * - * @param \Drupal\Core\Path\AliasStorageInterface $storage - * The alias storage service. + * @param \Drupal\Core\Path\AliasRepositoryInterface $alias_repository + * The path alias repository. * @param \Drupal\Core\Path\AliasWhitelistInterface $whitelist * The whitelist implementation to use. * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager @@ -104,8 +112,12 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { * @param \Drupal\Core\Cache\CacheBackendInterface $cache * Cache backend. */ - public function __construct(AliasStorageInterface $storage, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) { - $this->storage = $storage; + public function __construct($alias_repository, AliasWhitelistInterface $whitelist, LanguageManagerInterface $language_manager, CacheBackendInterface $cache) { + if (!$alias_repository instanceof AliasRepositoryInterface) { + @trigger_error('Passing the path.alias_storage service to AliasManager::__construct() is deprecated in drupal:8.8.0 and will be removed before drupal:9.0.0. Pass the new dependencies instead. See https://www.drupal.org/node/3013865.', E_USER_DEPRECATED); + $alias_repository = \Drupal::service('path.alias_repository'); + } + $this->pathAliasRepository = $alias_repository; $this->languageManager = $language_manager; $this->whitelist = $whitelist; $this->cache = $cache; @@ -166,9 +178,9 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { } // Look for path in storage. - if ($path = $this->storage->lookupPathSource($alias, $langcode)) { - $this->lookupMap[$langcode][$path] = $alias; - return $path; + if ($path_alias = $this->pathAliasRepository->lookupByAlias($alias, $langcode)) { + $this->lookupMap[$langcode][$path_alias['path']] = $alias; + return $path_alias['path']; } // We can't record anything into $this->lookupMap because we didn't find any @@ -220,7 +232,7 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { // Load paths from cache. if (!empty($this->preloadedPathLookups[$langcode])) { - $this->lookupMap[$langcode] = $this->storage->preloadPathAlias($this->preloadedPathLookups[$langcode], $langcode); + $this->lookupMap[$langcode] = $this->pathAliasRepository->preloadPathAlias($this->preloadedPathLookups[$langcode], $langcode); // Keep a record of paths with no alias to avoid querying twice. $this->noAlias[$langcode] = array_flip(array_diff_key($this->preloadedPathLookups[$langcode], array_keys($this->lookupMap[$langcode]))); } @@ -237,9 +249,9 @@ class AliasManager implements AliasManagerInterface, CacheDecoratorInterface { } // Try to load alias from storage. - if ($alias = $this->storage->lookupPathAlias($path, $langcode)) { - $this->lookupMap[$langcode][$path] = $alias; - return $alias; + if ($path_alias = $this->pathAliasRepository->lookupBySystemPath($path, $langcode)) { + $this->lookupMap[$langcode][$path] = $path_alias['alias']; + return $path_alias['alias']; } // We can't record anything into $this->lookupMap because we didn't find any diff --git a/core/lib/Drupal/Core/Path/AliasManagerInterface.php b/core/lib/Drupal/Core/Path/AliasManagerInterface.php index a3096fc64fb..6ede000e419 100644 --- a/core/lib/Drupal/Core/Path/AliasManagerInterface.php +++ b/core/lib/Drupal/Core/Path/AliasManagerInterface.php @@ -4,8 +4,6 @@ namespace Drupal\Core\Path; /** * Find an alias for a path and vice versa. - * - * @see \Drupal\Core\Path\AliasStorageInterface */ interface AliasManagerInterface { diff --git a/core/lib/Drupal/Core/Path/AliasRepository.php b/core/lib/Drupal/Core/Path/AliasRepository.php new file mode 100644 index 00000000000..dbb14d800ca --- /dev/null +++ b/core/lib/Drupal/Core/Path/AliasRepository.php @@ -0,0 +1,142 @@ +connection = $connection; + } + + /** + * {@inheritdoc} + */ + public function preloadPathAlias($preloaded, $langcode) { + $select = $this->getBaseQuery() + ->fields('base_table', ['path', 'alias']); + + if (!empty($preloaded)) { + $conditions = new Condition('OR'); + foreach ($preloaded as $preloaded_item) { + $conditions->condition('base_table.path', $this->connection->escapeLike($preloaded_item), 'LIKE'); + } + $select->condition($conditions); + } + + $this->addLanguageFallback($select, $langcode); + + // We order by ID ASC so that fetchAllKeyed() returns the most recently + // created alias for each source. Subsequent queries using fetchField() must + // use ID DESC to have the same effect. + $select->orderBy('base_table.id', 'ASC'); + + return $select->execute()->fetchAllKeyed(); + } + + /** + * {@inheritdoc} + */ + public function lookupBySystemPath($path, $langcode) { + // See the queries above. Use LIKE for case-insensitive matching. + $select = $this->getBaseQuery() + ->fields('base_table', ['id', 'path', 'alias', 'langcode']) + ->condition('base_table.path', $this->connection->escapeLike($path), 'LIKE'); + + $this->addLanguageFallback($select, $langcode); + + $select->orderBy('base_table.id', 'DESC'); + + return $select->execute()->fetchAssoc() ?: NULL; + } + + /** + * {@inheritdoc} + */ + public function lookupByAlias($alias, $langcode) { + // See the queries above. Use LIKE for case-insensitive matching. + $select = $this->getBaseQuery() + ->fields('base_table', ['id', 'path', 'alias', 'langcode']) + ->condition('base_table.alias', $this->connection->escapeLike($alias), 'LIKE'); + + $this->addLanguageFallback($select, $langcode); + + $select->orderBy('base_table.id', 'DESC'); + + return $select->execute()->fetchAssoc() ?: NULL; + } + + /** + * {@inheritdoc} + */ + public function pathHasMatchingAlias($initial_substring) { + $query = $this->getBaseQuery(); + $query->addExpression(1); + + return (bool) $query + ->condition('base_table.path', $this->connection->escapeLike($initial_substring) . '%', 'LIKE') + ->range(0, 1) + ->execute() + ->fetchField(); + } + + /** + * Returns a SELECT query for the path_alias base table. + * + * @return \Drupal\Core\Database\Query\SelectInterface + * A Select query object. + */ + protected function getBaseQuery() { + $query = $this->connection->select('path_alias', 'base_table'); + $query->condition('base_table.status', 1); + + return $query; + } + + /** + * Adds path alias language fallback conditions to a select query object. + * + * @param \Drupal\Core\Database\Query\SelectInterface $query + * A Select query object. + * @param string $langcode + * Language code to search the path with. If there's no path defined for + * that language it will search paths without language. + */ + protected function addLanguageFallback(SelectInterface $query, $langcode) { + // Always get the language-specific alias before the language-neutral one. + // For example 'de' is less than 'und' so the order needs to be ASC, while + // 'xx-lolspeak' is more than 'und' so the order needs to be DESC. + $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; + if ($langcode === LanguageInterface::LANGCODE_NOT_SPECIFIED) { + array_pop($langcode_list); + } + elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) { + $query->orderBy('base_table.langcode', 'DESC'); + } + else { + $query->orderBy('base_table.langcode', 'ASC'); + } + $query->condition('base_table.langcode', $langcode_list, 'IN'); + } + +} diff --git a/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php b/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php new file mode 100644 index 00000000000..15febd495b1 --- /dev/null +++ b/core/lib/Drupal/Core/Path/AliasRepositoryInterface.php @@ -0,0 +1,81 @@ + 'path.alias_storage']; + /** * The Key/Value Store to use for state. * @@ -20,11 +28,11 @@ class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface { protected $state; /** - * The Path CRUD service. + * The path alias repository. * - * @var \Drupal\Core\Path\AliasStorageInterface + * @var \Drupal\Core\Path\AliasRepositoryInterface */ - protected $aliasStorage; + protected $pathAliasRepository; /** * Constructs an AliasWhitelist object. @@ -37,13 +45,18 @@ class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface { * The lock backend. * @param \Drupal\Core\State\StateInterface $state * The state keyvalue store. - * @param \Drupal\Core\Path\AliasStorageInterface $alias_storage - * The alias storage service. + * @param \Drupal\Core\Path\AliasRepositoryInterface $alias_repository + * The path alias repository. */ - public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, StateInterface $state, AliasStorageInterface $alias_storage) { + public function __construct($cid, CacheBackendInterface $cache, LockBackendInterface $lock, StateInterface $state, $alias_repository) { parent::__construct($cid, $cache, $lock); $this->state = $state; - $this->aliasStorage = $alias_storage; + + if (!$alias_repository instanceof AliasRepositoryInterface) { + @trigger_error('Passing the path.alias_storage service to AliasWhitelist::__construct() is deprecated in drupal:8.8.0 and will be removed before drupal:9.0.0. Pass the new dependencies instead. See https://www.drupal.org/node/3013865.', E_USER_DEPRECATED); + $alias_repository = \Drupal::service('path.alias_repository'); + } + $this->pathAliasRepository = $alias_repository; } /** @@ -101,7 +114,7 @@ class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface { * {@inheritdoc} */ public function resolveCacheMiss($root) { - $exists = $this->aliasStorage->pathHasMatchingAlias('/' . $root); + $exists = $this->pathAliasRepository->pathHasMatchingAlias('/' . $root); $this->storage[$root] = $exists; $this->persist($root); if ($exists) { diff --git a/core/modules/link/tests/src/Functional/LinkFieldTest.php b/core/modules/link/tests/src/Functional/LinkFieldTest.php index e5aede5c9c6..fb3680af66f 100644 --- a/core/modules/link/tests/src/Functional/LinkFieldTest.php +++ b/core/modules/link/tests/src/Functional/LinkFieldTest.php @@ -13,6 +13,7 @@ use Drupal\link\LinkItemInterface; use Drupal\node\NodeInterface; use Drupal\Tests\BrowserTestBase; use Drupal\field\Entity\FieldStorageConfig; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Tests link field widgets and formatters. @@ -21,6 +22,8 @@ use Drupal\field\Entity\FieldStorageConfig; */ class LinkFieldTest extends BrowserTestBase { + use PathAliasTestTrait; + /** * Modules to enable. * @@ -100,7 +103,7 @@ class LinkFieldTest extends BrowserTestBase { $this->assertRaw('placeholder="http://example.com"'); // Create a path alias. - \Drupal::service('path.alias_storage')->save('/admin', '/a/path/alias'); + $this->createPathAlias('/admin', '/a/path/alias'); // Create a node to test the link widget. $node = $this->drupalCreateNode(); diff --git a/core/modules/locale/tests/src/Functional/LocalePathTest.php b/core/modules/locale/tests/src/Functional/LocalePathTest.php index 074a82c47d1..588488eb0b2 100644 --- a/core/modules/locale/tests/src/Functional/LocalePathTest.php +++ b/core/modules/locale/tests/src/Functional/LocalePathTest.php @@ -5,14 +5,18 @@ namespace Drupal\Tests\locale\Functional; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Url; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Tests you can configure a language for individual URL aliases. * * @group locale + * @group path */ class LocalePathTest extends BrowserTestBase { + use PathAliasTestTrait; + /** * Modules to enable. * @@ -96,44 +100,24 @@ class LocalePathTest extends BrowserTestBase { $custom_path = $this->randomMachineName(8); // Check priority of language for alias by source path. - $edit = [ - 'path[0][value]' => '/node/' . $node->id(), - 'alias[0][value]' => '/' . $custom_path, - 'langcode[0][value]' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - ]; - $this->container->get('path.alias_storage')->save($edit['path[0][value]'], $edit['alias[0][value]'], $edit['langcode[0][value]']); + $path_alias = $this->createPathAlias('/node/' . $node->id(), '/' . $custom_path, LanguageInterface::LANGCODE_NOT_SPECIFIED); $lookup_path = $this->container->get('path.alias_manager')->getAliasByPath('/node/' . $node->id(), 'en'); $this->assertEqual('/' . $english_path, $lookup_path, 'English language alias has priority.'); // Same check for language 'xx'. $lookup_path = $this->container->get('path.alias_manager')->getAliasByPath('/node/' . $node->id(), $prefix); $this->assertEqual('/' . $custom_language_path, $lookup_path, 'Custom language alias has priority.'); - $path_alias = [ - 'path' => $edit['path[0][value]'], - 'alias' => $edit['alias[0][value]'], - 'langcode' => $edit['langcode[0][value]'], - ]; - $this->container->get('path.alias_storage')->delete($path_alias); + $path_alias->delete(); // Create language nodes to check priority of aliases. $first_node = $this->drupalCreateNode(['type' => 'page', 'promote' => 1, 'langcode' => 'en']); $second_node = $this->drupalCreateNode(['type' => 'page', 'promote' => 1, 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED]); // Assign a custom path alias to the first node with the English language. - $edit = [ - 'path[0][value]' => '/node/' . $first_node->id(), - 'alias[0][value]' => '/' . $custom_path, - 'langcode[0][value]' => $first_node->language()->getId(), - ]; - $this->container->get('path.alias_storage')->save($edit['path[0][value]'], $edit['alias[0][value]'], $edit['langcode[0][value]']); + $this->createPathAlias('/node/' . $first_node->id(), '/' . $custom_path, $first_node->language()->getId()); // Assign a custom path alias to second node with // LanguageInterface::LANGCODE_NOT_SPECIFIED. - $edit = [ - 'path[0][value]' => '/node/' . $second_node->id(), - 'alias[0][value]' => '/' . $custom_path, - 'langcode[0][value]' => $second_node->language()->getId(), - ]; - $this->container->get('path.alias_storage')->save($edit['path[0][value]'], $edit['alias[0][value]'], $edit['langcode[0][value]']); + $this->createPathAlias('/node/' . $second_node->id(), '/' . $custom_path, $second_node->language()->getId()); // Test that both node titles link to our path alias. $this->drupalGet('admin/content'); diff --git a/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php b/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php index c0cdba31b80..a00bbd18381 100644 --- a/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php +++ b/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php @@ -3,18 +3,21 @@ namespace Drupal\Tests\menu_link_content\Kernel; use Drupal\Core\DependencyInjection\ContainerBuilder; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Menu\MenuTreeParameters; use Drupal\menu_link_content\Entity\MenuLinkContent; use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Ensures that the menu tree adapts to path alias changes. * * @group menu_link_content + * @group path */ class PathAliasMenuLinkContentTest extends KernelTestBase { + use PathAliasTestTrait; + /** * {@inheritdoc} */ @@ -52,11 +55,7 @@ class PathAliasMenuLinkContentTest extends KernelTestBase { public function testPathAliasChange() { \Drupal::service('router.builder')->rebuild(); - /** @var \Drupal\Core\Path\AliasStorageInterface $path_alias_storage */ - $path_alias_storage = \Drupal::service('path.alias_storage'); - $alias = $path_alias_storage->save('/test-page', '/my-blog'); - $pid = $alias['pid']; - + $path_alias = $this->createPathAlias('/test-page', '/my-blog'); $menu_link_content = MenuLinkContent::create([ 'title' => 'Menu title', 'link' => ['uri' => 'internal:/my-blog'], @@ -68,13 +67,15 @@ class PathAliasMenuLinkContentTest extends KernelTestBase { $this->assertEqual('test_page_test.test_page', $tree[$menu_link_content->getPluginId()]->link->getPluginDefinition()['route_name']); // Saving an alias should clear the alias manager cache. - $path_alias_storage->save('/test-render-title', '/my-blog', LanguageInterface::LANGCODE_NOT_SPECIFIED, $pid); + $path_alias->setPath('/test-render-title'); + $path_alias->setAlias('/my-blog'); + $path_alias->save(); $tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters()); $this->assertEqual('test_page_test.render_title', $tree[$menu_link_content->getPluginId()]->link->getPluginDefinition()['route_name']); // Delete the alias. - $path_alias_storage->delete(['pid' => $pid]); + $path_alias->delete(); $tree = \Drupal::menuTree()->load('tools', new MenuTreeParameters()); $this->assertTrue(isset($tree[$menu_link_content->getPluginId()])); $this->assertEqual('', $tree[$menu_link_content->getPluginId()]->link->getRouteName()); diff --git a/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php b/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php index 1985499dee5..bdc20e60aa4 100644 --- a/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php +++ b/core/modules/path/src/Plugin/Field/FieldType/PathFieldItemList.php @@ -4,7 +4,6 @@ namespace Drupal\path\Plugin\Field\FieldType; use Drupal\Core\Access\AccessResult; use Drupal\Core\Field\FieldItemList; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Session\AccountInterface; use Drupal\Core\TypedData\ComputedItemListTrait; @@ -27,21 +26,15 @@ class PathFieldItemList extends FieldItemList { $entity = $this->getEntity(); if (!$entity->isNew()) { - $conditions = [ - 'source' => '/' . $entity->toUrl()->getInternalPath(), - 'langcode' => $this->getLangcode(), - ]; - $alias = \Drupal::service('path.alias_storage')->load($conditions); - if ($alias === FALSE) { - // Fall back to non-specific language. - if ($this->getLangcode() !== LanguageInterface::LANGCODE_NOT_SPECIFIED) { - $conditions['langcode'] = LanguageInterface::LANGCODE_NOT_SPECIFIED; - $alias = \Drupal::service('path.alias_storage')->load($conditions); - } - } + /** @var \Drupal\Core\Path\AliasRepositoryInterface $path_alias_repository */ + $path_alias_repository = \Drupal::service('path.alias_repository'); - if ($alias) { - $value = $alias; + if ($path_alias = $path_alias_repository->lookupBySystemPath('/' . $entity->toUrl()->getInternalPath(), $this->getLangcode())) { + $value = [ + 'alias' => $path_alias['alias'], + 'pid' => $path_alias['id'], + 'langcode' => $path_alias['langcode'], + ]; } } @@ -64,11 +57,12 @@ class PathFieldItemList extends FieldItemList { public function delete() { // Delete all aliases associated with this entity in the current language. $entity = $this->getEntity(); - $conditions = [ - 'source' => '/' . $entity->toUrl()->getInternalPath(), + $path_alias_storage = \Drupal::entityTypeManager()->getStorage('path_alias'); + $entities = $path_alias_storage->loadByProperties([ + 'path' => '/' . $entity->toUrl()->getInternalPath(), 'langcode' => $entity->language()->getId(), - ]; - \Drupal::service('path.alias_storage')->delete($conditions); + ]); + $path_alias_storage->delete($entities); } } diff --git a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php index 090edd4a9d8..3962175b261 100644 --- a/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php +++ b/core/modules/path/src/Plugin/Field/FieldWidget/PathWidget.php @@ -87,15 +87,22 @@ class PathWidget extends WidgetBase { if (!empty($alias)) { $form_state->setValueForElement($element['alias'], $alias); - // Validate that the submitted alias does not exist yet. - $is_exists = \Drupal::service('path.alias_storage')->aliasExists($alias, $element['langcode']['#value'], $element['source']['#value']); - if ($is_exists) { - $form_state->setError($element['alias'], t('The alias is already in use.')); - } - } + /** @var \Drupal\Core\Path\PathAliasInterface $path_alias */ + $path_alias = \Drupal::entityTypeManager()->getStorage('path_alias')->create([ + 'path' => $element['source']['#value'], + 'alias' => $alias, + 'langcode' => $element['langcode']['#value'], + ]); + $violations = $path_alias->validate(); - if ($alias && $alias[0] !== '/') { - $form_state->setError($element['alias'], t('The alias needs to start with a slash.')); + foreach ($violations as $violation) { + // Newly created entities do not have a system path yet, so we need to + // disregard some violations. + if (!$path_alias->getPath() && $violation->getPropertyPath() === 'path') { + continue; + } + $form_state->setError($element['alias'], $violation->getMessage()); + } } } diff --git a/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php b/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php index 7714cbd1c4f..b2e7e0d758a 100644 --- a/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php +++ b/core/modules/path/src/Plugin/migrate/source/UrlAliasBase.php @@ -14,7 +14,7 @@ abstract class UrlAliasBase extends DrupalSqlBase { */ public function query() { // The order of the migration is significant since - // \Drupal\Core\Path\AliasStorage::lookupPathAlias() orders by pid before + // \Drupal\Core\Path\AliasRepository::lookupPathAlias() orders by pid before // returning a result. Postgres does not automatically order by primary key // therefore we need to add a specific order by. return $this->select('url_alias', 'ua')->fields('ua')->orderBy('pid'); diff --git a/core/modules/path/tests/src/Functional/PathAliasTest.php b/core/modules/path/tests/src/Functional/PathAliasTest.php index 80ce4c385bf..71e5783b9a4 100644 --- a/core/modules/path/tests/src/Functional/PathAliasTest.php +++ b/core/modules/path/tests/src/Functional/PathAliasTest.php @@ -283,7 +283,7 @@ class PathAliasTest extends PathTestBase { $this->drupalPostForm('node/' . $node2->id() . '/edit', $edit, t('Save')); // Confirm that the alias didn't make a duplicate. - $this->assertText(t('The alias is already in use.'), 'Attempt to moved alias was rejected.'); + $this->assertSession()->pageTextContains("The alias {$edit['path[0][alias]']} is already in use in this language."); // Delete alias. $this->drupalPostForm('node/' . $node1->id() . '/edit', ['path[0][alias]' => ''], t('Save')); @@ -326,7 +326,7 @@ class PathAliasTest extends PathTestBase { // Delete the node and check that the path alias is also deleted. $node5->delete(); - $path_alias = \Drupal::service('path.alias_storage')->lookupPathAlias('/node/' . $node5->id(), $node5->language()->getId()); + $path_alias = \Drupal::service('path.alias_repository')->lookUpBySystemPath('/node/' . $node5->id(), $node5->language()->getId()); $this->assertFalse($path_alias, 'Alias was successfully deleted when the referenced node was deleted.'); // Create sixth test node. @@ -388,7 +388,7 @@ class PathAliasTest extends PathTestBase { // Now create another node and try to set the same alias. $node_two = $this->drupalCreateNode(); $this->drupalPostForm('node/' . $node_two->id() . '/edit', $edit, t('Save')); - $this->assertText(t('The alias is already in use.')); + $this->assertSession()->pageTextContains("The alias {$edit['path[0][alias]']} is already in use in this language."); $this->assertFieldByXPath("//input[@name='path[0][alias]' and contains(@class, 'error')]", $edit['path[0][alias]'], 'Textfield exists and has the error class.'); // Behavior here differs with the inline_form_errors module enabled. @@ -399,7 +399,7 @@ class PathAliasTest extends PathTestBase { // Attempt to edit the second node again, as before. $this->drupalPostForm('node/' . $node_two->id() . '/edit', $edit, t('Preview')); // This error should still be present next to the field. - $this->assertSession()->pageTextContains(t('The alias is already in use.'), 'Field error found with expected text.'); + $this->assertSession()->pageTextContains("The alias {$edit['path[0][alias]']} is already in use in this language."); // The validation error set for the page should include this text. $this->assertSession()->pageTextContains(t('1 error has been found: URL alias'), 'Form error found with expected text.'); // The text 'URL alias' should be a link. diff --git a/core/modules/path/tests/src/Functional/PathLanguageTest.php b/core/modules/path/tests/src/Functional/PathLanguageTest.php index 3d7a2d6199e..ddb512fd799 100644 --- a/core/modules/path/tests/src/Functional/PathLanguageTest.php +++ b/core/modules/path/tests/src/Functional/PathLanguageTest.php @@ -184,11 +184,11 @@ class PathLanguageTest extends PathTestBase { // Confirm that the alias is removed if the translation is deleted. $english_node->removeTranslation('fr'); $english_node->save(); - $this->assertFalse($this->container->get('path.alias_storage')->aliasExists('/' . $french_alias, 'fr'), 'Alias for French translation is removed when translation is deleted.'); + $this->assertPathAliasNotExists('/' . $french_alias, 'fr', NULL, 'Alias for French translation is removed when translation is deleted.'); // Check that the English alias still works. $this->drupalGet($english_alias); - $this->assertTrue($this->container->get('path.alias_storage')->aliasExists('/' . $english_alias, 'en'), 'English alias is not deleted when French translation is removed.'); + $this->assertPathAliasExists('/' . $english_alias, 'en', NULL, 'English alias is not deleted when French translation is removed.'); $this->assertText($english_node->body->value, 'English alias still works'); } diff --git a/core/modules/path/tests/src/Functional/PathTestBase.php b/core/modules/path/tests/src/Functional/PathTestBase.php index d4b8826d58d..ca6f03c9016 100644 --- a/core/modules/path/tests/src/Functional/PathTestBase.php +++ b/core/modules/path/tests/src/Functional/PathTestBase.php @@ -3,12 +3,15 @@ namespace Drupal\Tests\path\Functional; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Provides a base class for testing the Path module. */ abstract class PathTestBase extends BrowserTestBase { + use PathAliasTestTrait; + /** * Modules to enable. * diff --git a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php index db63c41104e..c38f3d1f89a 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php @@ -2,9 +2,11 @@ namespace Drupal\Tests\path\Kernel\Migrate\d6; +use Drupal\Core\Path\PathAliasInterface; use Drupal\migrate\Plugin\MigrateIdMapInterface; use Drupal\Core\Database\Database; use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * URL alias migration. @@ -13,6 +15,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d6\MigrateDrupal6TestBase; */ class MigrateUrlAliasTest extends MigrateDrupal6TestBase { + use PathAliasTestTrait; + /** * {@inheritdoc} */ @@ -47,20 +51,20 @@ class MigrateUrlAliasTest extends MigrateDrupal6TestBase { } /** - * Assert a path. + * Asserts that a path alias matches a set of conditions. * - * @param string $pid - * The path id. + * @param int $pid + * The path alias ID. * @param array $conditions * The path conditions. - * @param array $path - * The path. + * @param \Drupal\Core\Path\PathAliasInterface $path_alias + * The path alias. */ - private function assertPath($pid, $conditions, $path) { - $this->assertTrue($path, "Path alias for " . $conditions['source'] . " successfully loaded."); - $this->assertIdentical($conditions['alias'], $path['alias']); - $this->assertIdentical($conditions['langcode'], $path['langcode']); - $this->assertIdentical($conditions['source'], $path['source']); + private function assertPath($pid, $conditions, PathAliasInterface $path_alias) { + $this->assertSame($pid, (int) $path_alias->id()); + $this->assertSame($conditions['alias'], $path_alias->getAlias()); + $this->assertSame($conditions['langcode'], $path_alias->get('langcode')->value); + $this->assertSame($conditions['path'], $path_alias->getPath()); } /** @@ -70,21 +74,21 @@ class MigrateUrlAliasTest extends MigrateDrupal6TestBase { $id_map = $this->getMigration('d6_url_alias')->getIdMap(); // Test that the field exists. $conditions = [ - 'source' => '/node/1', + 'path' => '/node/1', 'alias' => '/alias-one', 'langcode' => 'af', ]; - $path = \Drupal::service('path.alias_storage')->load($conditions); - $this->assertPath('1', $conditions, $path); - $this->assertIdentical($id_map->lookupDestinationIds([$path['pid']]), [['1']], "Test IdMap"); + $path_alias = $this->loadPathAliasByConditions($conditions); + $this->assertPath(1, $conditions, $path_alias); + $this->assertSame([['1']], $id_map->lookupDestinationIds([$path_alias->id()]), "Test IdMap"); $conditions = [ - 'source' => '/node/2', + 'path' => '/node/2', 'alias' => '/alias-two', 'langcode' => 'en', ]; - $path = \Drupal::service('path.alias_storage')->load($conditions); - $this->assertPath('2', $conditions, $path); + $path_alias = $this->loadPathAliasByConditions($conditions); + $this->assertPath(2, $conditions, $path_alias); // Test that we can re-import using the UrlAlias destination. Database::getConnection('default', 'migrate') @@ -100,54 +104,53 @@ class MigrateUrlAliasTest extends MigrateDrupal6TestBase { $migration = $this->getMigration('d6_url_alias'); $this->executeMigration($migration); - $path = \Drupal::service('path.alias_storage')->load(['pid' => $path['pid']]); + $path_alias = $this->loadPathAliasByConditions(['id' => $path_alias->id()]); $conditions['alias'] = '/new-url-alias'; - $this->assertPath('2', $conditions, $path); + $this->assertPath(2, $conditions, $path_alias); $conditions = [ - 'source' => '/node/3', + 'path' => '/node/3', 'alias' => '/alias-three', 'langcode' => 'und', ]; - $path = \Drupal::service('path.alias_storage')->load($conditions); - $this->assertPath('3', $conditions, $path); + $path_alias = $this->loadPathAliasByConditions($conditions); + $this->assertPath(3, $conditions, $path_alias); - $path = \Drupal::service('path.alias_storage')->load(['alias' => '/source-noslash']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/source-noslash']); $conditions = [ - 'source' => '/admin', + 'path' => '/admin', 'alias' => '/source-noslash', 'langcode' => 'und', ]; - $this->assertPath('2', $conditions, $path); + $this->assertPath(8, $conditions, $path_alias); } /** * Test the URL alias migration with translated nodes. */ public function testUrlAliasWithTranslatedNodes() { - $alias_storage = $this->container->get('path.alias_storage'); - // Alias for the 'The Real McCoy' node in English. - $path = $alias_storage->load(['alias' => '/the-real-mccoy']); - $this->assertSame('/node/10', $path['source']); - $this->assertSame('en', $path['langcode']); + + $path_alias = $this->loadPathAliasByConditions(['alias' => '/the-real-mccoy']); + $this->assertSame('/node/10', $path_alias->getPath()); + $this->assertSame('en', $path_alias->get('langcode')->value); // Alias for the 'The Real McCoy' French translation, // which should now point to node/10 instead of node/11. - $path = $alias_storage->load(['alias' => '/le-vrai-mccoy']); - $this->assertSame('/node/10', $path['source']); - $this->assertSame('fr', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/le-vrai-mccoy']); + $this->assertSame('/node/10', $path_alias->getPath()); + $this->assertSame('fr', $path_alias->get('langcode')->value); // Alias for the 'Abantu zulu' node in Zulu. - $path = $alias_storage->load(['alias' => '/abantu-zulu']); - $this->assertSame('/node/12', $path['source']); - $this->assertSame('zu', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/abantu-zulu']); + $this->assertSame('/node/12', $path_alias->getPath()); + $this->assertSame('zu', $path_alias->get('langcode')->value); // Alias for the 'Abantu zulu' English translation, // which should now point to node/12 instead of node/13. - $path = $alias_storage->load(['alias' => '/the-zulu-people']); - $this->assertSame('/node/12', $path['source']); - $this->assertSame('en', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/the-zulu-people']); + $this->assertSame('/node/12', $path_alias->getPath()); + $this->assertSame('en', $path_alias->get('langcode')->value); } } diff --git a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php index e48996e5186..307e5172f6c 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php @@ -32,29 +32,27 @@ class MigrateUrlAliasTest extends MigrateUrlAliasTestBase { * Test the URL alias migration with translated nodes. */ public function testUrlAliasWithTranslatedNodes() { - $alias_storage = $this->container->get('path.alias_storage'); - // Alias for the 'The thing about Deep Space 9' node in English. - $path = $alias_storage->load(['alias' => '/deep-space-9']); - $this->assertSame('/node/2', $path['source']); - $this->assertSame('en', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/deep-space-9']); + $this->assertSame('/node/2', $path_alias->getPath()); + $this->assertSame('en', $path_alias->get('langcode')->value); // Alias for the 'The thing about Deep Space 9' Icelandic translation, // which should now point to node/2 instead of node/3. - $path = $alias_storage->load(['alias' => '/deep-space-9-is']); - $this->assertSame('/node/2', $path['source']); - $this->assertSame('is', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/deep-space-9-is']); + $this->assertSame('/node/2', $path_alias->getPath()); + $this->assertSame('is', $path_alias->get('langcode')->value); // Alias for the 'The thing about Firefly' node in Icelandic. - $path = $alias_storage->load(['alias' => '/firefly-is']); - $this->assertSame('/node/4', $path['source']); - $this->assertSame('is', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/firefly-is']); + $this->assertSame('/node/4', $path_alias->getPath()); + $this->assertSame('is', $path_alias->get('langcode')->value); // Alias for the 'The thing about Firefly' English translation, // which should now point to node/4 instead of node/5. - $path = $alias_storage->load(['alias' => '/firefly']); - $this->assertSame('/node/4', $path['source']); - $this->assertSame('en', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/firefly']); + $this->assertSame('/node/4', $path_alias->getPath()); + $this->assertSame('en', $path_alias->get('langcode')->value); } } diff --git a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php index 315063ea288..5d9ca426b56 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTestBase.php @@ -3,6 +3,7 @@ namespace Drupal\Tests\path\Kernel\Migrate\d7; use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Tests URL alias migration. @@ -11,6 +12,8 @@ use Drupal\Tests\migrate_drupal\Kernel\d7\MigrateDrupal7TestBase; */ abstract class MigrateUrlAliasTestBase extends MigrateDrupal7TestBase { + use PathAliasTestTrait; + /** * {@inheritdoc} */ @@ -45,21 +48,19 @@ abstract class MigrateUrlAliasTestBase extends MigrateDrupal7TestBase { * Test the URL alias migration. */ public function testUrlAlias() { - $alias_storage = $this->container->get('path.alias_storage'); - - $path = $alias_storage->load([ - 'source' => '/taxonomy/term/4', + $path_alias = $this->loadPathAliasByConditions([ + 'path' => '/taxonomy/term/4', 'alias' => '/term33', 'langcode' => 'und', ]); - $this->assertIdentical('/taxonomy/term/4', $path['source']); - $this->assertIdentical('/term33', $path['alias']); - $this->assertIdentical('und', $path['langcode']); + $this->assertIdentical('/taxonomy/term/4', $path_alias->getPath()); + $this->assertIdentical('/term33', $path_alias->getAlias()); + $this->assertIdentical('und', $path_alias->language()->getId()); // Alias with no slash. - $path = $alias_storage->load(['alias' => '/source-noslash']); - $this->assertSame('/admin', $path['source']); - $this->assertSame('und', $path['langcode']); + $path_alias = $this->loadPathAliasByConditions(['alias' => '/source-noslash']); + $this->assertSame('/admin', $path_alias->getPath()); + $this->assertSame('und', $path_alias->language()->getId()); } } diff --git a/core/modules/path/tests/src/Kernel/PathItemTest.php b/core/modules/path/tests/src/Kernel/PathItemTest.php index f001dc6bcfe..5a39d698b3f 100644 --- a/core/modules/path/tests/src/Kernel/PathItemTest.php +++ b/core/modules/path/tests/src/Kernel/PathItemTest.php @@ -44,9 +44,8 @@ class PathItemTest extends KernelTestBase { * Test creating, loading, updating and deleting aliases through PathItem. */ public function testPathItem() { - - /** @var \Drupal\Core\Path\AliasStorageInterface $alias_storage */ - $alias_storage = \Drupal::service('path.alias_storage'); + /** @var \Drupal\Core\Path\AliasRepositoryInterface $alias_repository */ + $alias_repository = \Drupal::service('path.alias_repository'); $node_storage = \Drupal::entityTypeManager()->getStorage('node'); @@ -62,8 +61,8 @@ class PathItemTest extends KernelTestBase { $this->assertFalse($node->get('path')->isEmpty()); $this->assertEquals('/foo', $node->get('path')->alias); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); - $this->assertEquals('/foo', $stored_alias); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); + $this->assertEquals('/foo', $stored_alias['alias']); $node_storage->resetCache(); @@ -97,10 +96,10 @@ class PathItemTest extends KernelTestBase { $translation = $loaded_node->getTranslation('de'); $this->assertEquals('/furchtbar', $translation->path->alias); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); - $this->assertEquals('/foo', $stored_alias); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $translation->language()->getId()); - $this->assertEquals('/furchtbar', $stored_alias); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); + $this->assertEquals('/foo', $stored_alias['alias']); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $translation->language()->getId()); + $this->assertEquals('/furchtbar', $stored_alias['alias']); $loaded_node->get('path')->alias = '/bar'; $this->assertFalse($loaded_node->get('path')->isEmpty()); @@ -123,11 +122,11 @@ class PathItemTest extends KernelTestBase { $this->assertFalse($loaded_node->get('path')->isEmpty()); $this->assertEquals('/bar', $loaded_node->get('path')->alias); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); - $this->assertEquals('/bar', $stored_alias); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); + $this->assertEquals('/bar', $stored_alias['alias']); - $old_alias = $alias_storage->lookupPathSource('/foo', $node->language()->getId()); - $this->assertFalse($old_alias); + $old_alias = $alias_repository->lookupByAlias('/foo', $node->language()->getId()); + $this->assertNull($old_alias); // Reload the node to make sure that it is possible to set a value // immediately after loading. @@ -140,19 +139,19 @@ class PathItemTest extends KernelTestBase { $loaded_node = $node_storage->load($node->id()); $this->assertFalse($loaded_node->get('path')->isEmpty()); $this->assertEquals('/foobar', $loaded_node->get('path')->alias); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); - $this->assertEquals('/foobar', $stored_alias); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); + $this->assertEquals('/foobar', $stored_alias['alias']); - $old_alias = $alias_storage->lookupPathSource('/bar', $node->language()->getId()); - $this->assertFalse($old_alias); + $old_alias = $alias_repository->lookupByAlias('/bar', $node->language()->getId()); + $this->assertNull($old_alias); $loaded_node->get('path')->alias = ''; $this->assertEquals('', $loaded_node->get('path')->alias); $loaded_node->save(); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); - $this->assertFalse($stored_alias); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); + $this->assertNull($stored_alias); // Check that reading, updating and reading the computed alias again in the // same request works without clearing any caches in between. @@ -162,16 +161,16 @@ class PathItemTest extends KernelTestBase { $this->assertFalse($loaded_node->get('path')->isEmpty()); $this->assertEquals('/foo', $loaded_node->get('path')->alias); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); - $this->assertEquals('/foo', $stored_alias); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); + $this->assertEquals('/foo', $stored_alias['alias']); $loaded_node->get('path')->alias = '/foobar'; $loaded_node->save(); $this->assertFalse($loaded_node->get('path')->isEmpty()); $this->assertEquals('/foobar', $loaded_node->get('path')->alias); - $stored_alias = $alias_storage->lookupPathAlias('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); - $this->assertEquals('/foobar', $stored_alias); + $stored_alias = $alias_repository->lookupBySystemPath('/' . $node->toUrl()->getInternalPath(), $node->language()->getId()); + $this->assertEquals('/foobar', $stored_alias['alias']); // Check that \Drupal\Core\Field\FieldItemList::equals() for the path field // type. diff --git a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php index f14109b539e..aa6a5432b1e 100644 --- a/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php +++ b/core/modules/shortcut/tests/src/Functional/ShortcutLinksTest.php @@ -8,6 +8,7 @@ use Drupal\Core\Url; use Drupal\shortcut\Entity\Shortcut; use Drupal\shortcut\Entity\ShortcutSet; use Drupal\Tests\block\Functional\AssertBlockAppearsTrait; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; use Drupal\views\Entity\View; /** @@ -18,6 +19,7 @@ use Drupal\views\Entity\View; class ShortcutLinksTest extends ShortcutTestBase { use AssertBlockAppearsTrait; + use PathAliasTestTrait; /** * Modules to enable. @@ -42,11 +44,7 @@ class ShortcutLinksTest extends ShortcutTestBase { $set = $this->set; // Create an alias for the node so we can test aliases. - $path = [ - 'source' => '/node/' . $this->node->id(), - 'alias' => '/' . $this->randomMachineName(8), - ]; - $this->container->get('path.alias_storage')->save($path['source'], $path['alias']); + $path_alias = $this->createPathAlias('/node/' . $this->node->id(), '/' . $this->randomMachineName(8)); // Create some paths to test. $test_cases = [ @@ -54,7 +52,7 @@ class ShortcutLinksTest extends ShortcutTestBase { '/admin', '/admin/config/system/site-information', '/node/' . $this->node->id() . '/edit', - $path['alias'], + $path_alias->getAlias(), '/router_test/test2', '/router_test/test3/value', ]; diff --git a/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php b/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php index 8a79fd7c4b4..6dc35713362 100644 --- a/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php +++ b/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php @@ -7,14 +7,17 @@ use Drupal\Core\Database\Database; use Drupal\Core\Url; use Drupal\Tests\BrowserTestBase; use Drupal\taxonomy\Entity\Term; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Tests altering the inbound path and the outbound path. * - * @group Path + * @group path */ class UrlAlterFunctionalTest extends BrowserTestBase { + use PathAliasTestTrait; + /** * Modules to enable. * @@ -43,8 +46,7 @@ class UrlAlterFunctionalTest extends BrowserTestBase { $this->assertUrlOutboundAlter("/user/$uid", "/user/$name"); // Test that a path always uses its alias. - $path = ['source' => "/user/$uid/test1", 'alias' => '/alias/test1']; - $this->container->get('path.alias_storage')->save($path['source'], $path['alias']); + $this->createPathAlias("/user/$uid/test1", '/alias/test1'); $this->rebuildContainer(); $this->assertUrlInboundAlter('/alias/test1', "/user/$uid/test1"); $this->assertUrlOutboundAlter("/user/$uid/test1", '/alias/test1'); diff --git a/core/modules/system/tests/src/Kernel/PathHooksTest.php b/core/modules/system/tests/src/Kernel/PathHooksTest.php index 0c4638cbad2..cef1211928d 100644 --- a/core/modules/system/tests/src/Kernel/PathHooksTest.php +++ b/core/modules/system/tests/src/Kernel/PathHooksTest.php @@ -2,13 +2,15 @@ namespace Drupal\Tests\system\Kernel; -use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Path\AliasManagerInterface; +use Drupal\Core\Path\Entity\PathAlias; use Drupal\KernelTests\KernelTestBase; use Prophecy\Argument; /** - * @group Drupal + * @coversDefaultClass \Drupal\Core\Path\Entity\PathAlias + * + * @group path */ class PathHooksTest extends KernelTestBase { @@ -27,38 +29,43 @@ class PathHooksTest extends KernelTestBase { } /** - * Test system_path_alias_*() correctly clears caches. + * Tests that the PathAlias entity clears caches correctly. + * + * @covers ::postSave + * @covers ::postDelete */ public function testPathHooks() { - $source = '/' . $this->randomMachineName(); - $alias = '/' . $this->randomMachineName(); + $path_alias = PathAlias::create([ + 'path' => '/' . $this->randomMachineName(), + 'alias' => '/' . $this->randomMachineName(), + ]); - // Check system_path_alias_insert(); + // Check \Drupal\Core\Path\Entity\PathAlias::postSave() for new path alias + // entities. $alias_manager = $this->prophesize(AliasManagerInterface::class); $alias_manager->cacheClear(Argument::any())->shouldBeCalledTimes(1); - $alias_manager->cacheClear($source)->shouldBeCalledTimes(1); + $alias_manager->cacheClear($path_alias->getPath())->shouldBeCalledTimes(1); \Drupal::getContainer()->set('path.alias_manager', $alias_manager->reveal()); - $alias_storage = \Drupal::service('path.alias_storage'); - $alias_storage->save($source, $alias); + $path_alias->save(); $new_source = '/' . $this->randomMachineName(); - $path = $alias_storage->load(['source' => $source]); - // Check system_path_alias_update(); + // Check \Drupal\Core\Path\Entity\PathAlias::postSave() for existing path + // alias entities. $alias_manager = $this->prophesize(AliasManagerInterface::class); $alias_manager->cacheClear(Argument::any())->shouldBeCalledTimes(2); - $alias_manager->cacheClear($source)->shouldBeCalledTimes(1); + $alias_manager->cacheClear($path_alias->getPath())->shouldBeCalledTimes(1); $alias_manager->cacheClear($new_source)->shouldBeCalledTimes(1); \Drupal::getContainer()->set('path.alias_manager', $alias_manager->reveal()); - $alias_storage->save($new_source, $alias, LanguageInterface::LANGCODE_NOT_SPECIFIED, $path['pid']); + $path_alias->setPath($new_source); + $path_alias->save(); - // Check system_path_alias_delete(); + // Check \Drupal\Core\Path\Entity\PathAlias::postDelete(). $alias_manager = $this->prophesize(AliasManagerInterface::class); $alias_manager->cacheClear(Argument::any())->shouldBeCalledTimes(1); $alias_manager->cacheClear($new_source)->shouldBeCalledTimes(1); \Drupal::getContainer()->set('path.alias_manager', $alias_manager->reveal()); - $alias_storage->delete(['pid' => $path['pid']]); - + $path_alias->delete(); } } diff --git a/core/modules/workspaces/src/AliasStorage.php b/core/modules/workspaces/src/WorkspacesAliasRepository.php similarity index 58% rename from core/modules/workspaces/src/AliasStorage.php rename to core/modules/workspaces/src/WorkspacesAliasRepository.php index 219eb7f4160..e1a1e828341 100644 --- a/core/modules/workspaces/src/AliasStorage.php +++ b/core/modules/workspaces/src/WorkspacesAliasRepository.php @@ -2,15 +2,12 @@ namespace Drupal\workspaces; -use Drupal\Core\Database\Connection; -use Drupal\Core\Entity\EntityTypeManagerInterface; -use Drupal\Core\Extension\ModuleHandlerInterface; -use Drupal\Core\Path\AliasStorage as CoreAliasStorage; +use Drupal\Core\Path\AliasRepository; /** * Provides workspace-specific path alias lookup queries. */ -class AliasStorage extends CoreAliasStorage { +class WorkspacesAliasRepository extends AliasRepository { /** * The workspace manager. @@ -20,20 +17,16 @@ class AliasStorage extends CoreAliasStorage { protected $workspaceManager; /** - * AliasStorage constructor. + * Sets the workspace manager. * - * @param \Drupal\Core\Database\Connection $connection - * A database connection for reading and writing path aliases. - * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler - * The module handler. - * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entity_type_manager - * The entity type manager. * @param \Drupal\workspaces\WorkspaceManagerInterface $workspace_manager * The workspace manager service. + * + * @return $this */ - public function __construct(Connection $connection, ModuleHandlerInterface $module_handler, EntityTypeManagerInterface $entity_type_manager, WorkspaceManagerInterface $workspace_manager) { - parent::__construct($connection, $module_handler, $entity_type_manager); + public function setWorkspacesManager(WorkspaceManagerInterface $workspace_manager) { $this->workspaceManager = $workspace_manager; + return $this; } /** diff --git a/core/modules/workspaces/src/WorkspacesServiceProvider.php b/core/modules/workspaces/src/WorkspacesServiceProvider.php index 598ae742d7c..1ed61747aaa 100644 --- a/core/modules/workspaces/src/WorkspacesServiceProvider.php +++ b/core/modules/workspaces/src/WorkspacesServiceProvider.php @@ -20,10 +20,10 @@ class WorkspacesServiceProvider extends ServiceProviderBase { $renderer_config['required_cache_contexts'][] = 'workspace'; $container->setParameter('renderer.config', $renderer_config); - // Replace the class of the 'path.alias_storage' service. - $container->getDefinition('path.alias_storage') - ->setClass(AliasStorage::class) - ->addArgument(new Reference('workspaces.manager')); + // Replace the class of the 'path.alias_repository' service. + $container->getDefinition('path.alias_repository') + ->setClass(WorkspacesAliasRepository::class) + ->addMethodCall('setWorkspacesManager', [new Reference('workspaces.manager')]); } } diff --git a/core/tests/Drupal/FunctionalTests/Installer/DistributionProfileTest.php b/core/tests/Drupal/FunctionalTests/Installer/DistributionProfileTest.php index 51e702f40b0..dce7ef51178 100644 --- a/core/tests/Drupal/FunctionalTests/Installer/DistributionProfileTest.php +++ b/core/tests/Drupal/FunctionalTests/Installer/DistributionProfileTest.php @@ -36,7 +36,7 @@ class DistributionProfileTest extends InstallerTestBase { $path = $this->siteDirectory . '/profiles/mydistro'; mkdir($path, 0777, TRUE); file_put_contents("$path/mydistro.info.yml", Yaml::encode($this->info)); - file_put_contents("$path/mydistro.install", "save('/user/1', '/myrootuser');}"); + file_put_contents("$path/mydistro.install", "getStorage('path_alias')->create(['path' => '/user/1', 'alias' => '/myrootuser'])->save();}"); } /** diff --git a/core/tests/Drupal/FunctionalTests/Routing/PathEncodedTest.php b/core/tests/Drupal/FunctionalTests/Routing/PathEncodedTest.php index a8aee89711f..bf33b18f309 100644 --- a/core/tests/Drupal/FunctionalTests/Routing/PathEncodedTest.php +++ b/core/tests/Drupal/FunctionalTests/Routing/PathEncodedTest.php @@ -4,14 +4,18 @@ namespace Drupal\FunctionalTests\Routing; use Drupal\Core\Url; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Tests url generation and routing for route paths with encoded characters. * + * @group path * @group routing */ class PathEncodedTest extends BrowserTestBase { + use PathAliasTestTrait; + /** * {@inheritdoc} */ @@ -35,12 +39,10 @@ class PathEncodedTest extends BrowserTestBase { 'path_encoded_test.atsign' => '/bloggy/@Dries', 'path_encoded_test.parens' => '/cat(box)', ]; - /** @var \Drupal\Core\Path\AliasStorageInterface $alias_storage */ - $alias_storage = $this->container->get('path.alias_storage'); $aliases = []; foreach ($route_paths as $route_name => $path) { $aliases[$route_name] = $this->randomMachineName(); - $alias_storage->save($path, '/' . $aliases[$route_name]); + $this->createPathAlias($path, '/' . $aliases[$route_name]); } foreach ($route_paths as $route_name => $path) { // The alias may be only a suffix of the generated path when the test is diff --git a/core/tests/Drupal/FunctionalTests/Routing/RouteCachingNonPathLanguageNegotiationTest.php b/core/tests/Drupal/FunctionalTests/Routing/RouteCachingNonPathLanguageNegotiationTest.php index 07286121b3c..37a07c69fe5 100644 --- a/core/tests/Drupal/FunctionalTests/Routing/RouteCachingNonPathLanguageNegotiationTest.php +++ b/core/tests/Drupal/FunctionalTests/Routing/RouteCachingNonPathLanguageNegotiationTest.php @@ -5,6 +5,7 @@ namespace Drupal\FunctionalTests\Routing; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Core\Language\LanguageInterface; use Drupal\Tests\BrowserTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Tests the route cache when the language is not in the path. @@ -13,6 +14,8 @@ use Drupal\Tests\BrowserTestBase; */ class RouteCachingNonPathLanguageNegotiationTest extends BrowserTestBase { + use PathAliasTestTrait; + /** * Modules to enable. * @@ -71,7 +74,7 @@ class RouteCachingNonPathLanguageNegotiationTest extends BrowserTestBase { // on the french page exist in english, no matter which language is // checked first. Create the alias after visiting frontpage to make sure // there is no existing cache entry for this that affects the tests. - \Drupal::service('path.alias_storage')->save('/user/' . $this->adminUser->id(), '/user-page', 'en'); + $this->createPathAlias('/user/' . $this->adminUser->id(), '/user-page', 'en'); $this->clickLink('French'); $this->drupalGet('user-page'); diff --git a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php index 47fa613ab26..f0f430e2c8c 100644 --- a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php +++ b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php @@ -8,6 +8,7 @@ use Drupal\Core\Config\DatabaseStorage; use Drupal\Core\Database\Database; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; use Drupal\user\Entity\User; use Symfony\Component\Console\Tester\CommandTester; use Symfony\Component\DependencyInjection\Reference; @@ -19,6 +20,8 @@ use Symfony\Component\DependencyInjection\Reference; */ class DbDumpTest extends KernelTestBase { + use PathAliasTestTrait; + /** * {@inheritdoc} */ @@ -109,7 +112,7 @@ class DbDumpTest extends KernelTestBase { $account->save(); // Create a path alias. - $this->container->get('path.alias_storage')->save('/user/' . $account->id(), '/user/example'); + $this->createPathAlias('/user/' . $account->id(), '/user/example'); // Create a cache table (this will create 'cache_discovery'). \Drupal::cache('discovery')->set('test', $this->data); diff --git a/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php b/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php deleted file mode 100644 index 2e986d18b3e..00000000000 --- a/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php +++ /dev/null @@ -1,83 +0,0 @@ -installEntitySchema('path_alias'); - $this->storage = $this->container->get('path.alias_storage'); - } - - /** - * @covers ::load - */ - public function testLoad() { - $this->storage->save('/test-source-Case', '/test-alias-Case'); - - $expected = [ - 'pid' => 1, - 'alias' => '/test-alias-Case', - 'source' => '/test-source-Case', - 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, - ]; - - $this->assertEquals($expected, $this->storage->load(['alias' => '/test-alias-Case'])); - $this->assertEquals($expected, $this->storage->load(['alias' => '/test-alias-case'])); - $this->assertEquals($expected, $this->storage->load(['source' => '/test-source-Case'])); - $this->assertEquals($expected, $this->storage->load(['source' => '/test-source-case'])); - } - - /** - * @covers ::lookupPathAlias - */ - public function testLookupPathAlias() { - $this->storage->save('/test-source-Case', '/test-alias'); - - $this->assertEquals('/test-alias', $this->storage->lookupPathAlias('/test-source-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); - $this->assertEquals('/test-alias', $this->storage->lookupPathAlias('/test-source-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); - } - - /** - * @covers ::lookupPathSource - */ - public function testLookupPathSource() { - $this->storage->save('/test-source', '/test-alias-Case'); - - $this->assertEquals('/test-source', $this->storage->lookupPathSource('/test-alias-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); - $this->assertEquals('/test-source', $this->storage->lookupPathSource('/test-alias-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); - } - - /** - * @covers ::aliasExists - */ - public function testAliasExists() { - $this->storage->save('/test-source-Case', '/test-alias-Case'); - - $this->assertTrue($this->storage->aliasExists('/test-alias-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); - $this->assertTrue($this->storage->aliasExists('/test-alias-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); - } - -} diff --git a/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php b/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php index bd26e1c6e0f..f37ba98b1eb 100644 --- a/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php +++ b/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php @@ -2,20 +2,24 @@ namespace Drupal\KernelTests\Core\Path; -use Drupal\Component\Render\FormattableMarkup; use Drupal\Core\Cache\MemoryCounterBackend; -use Drupal\Core\Database\Database; +use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Path\AliasManager; use Drupal\Core\Path\AliasWhitelist; use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; /** * Tests path alias CRUD and lookup functionality. * - * @group Path + * @coversDefaultClass \Drupal\Core\Path\AliasRepository + * + * @group path */ class AliasTest extends KernelTestBase { + use PathAliasTestTrait; + /** * {@inheritdoc} */ @@ -29,166 +33,76 @@ class AliasTest extends KernelTestBase { $this->installEntitySchema('path_alias'); } - public function testCRUD() { - // Prepare database table. - $connection = Database::getConnection(); + /** + * @covers ::lookupBySystemPath + */ + public function testLookupBySystemPath() { + $this->createPathAlias('/test-source-Case', '/test-alias'); - // Create Path object. - $aliasStorage = $this->container->get('path.alias_storage'); - - $aliases = $this->sampleUrlAliases(); - - // Create a few aliases - foreach ($aliases as $idx => $alias) { - $aliasStorage->save($alias['source'], $alias['alias'], $alias['langcode']); - - $result = $connection->query('SELECT * FROM {path_alias} WHERE path = :path AND alias= :alias AND langcode = :langcode', [':path' => $alias['source'], ':alias' => $alias['alias'], ':langcode' => $alias['langcode']]); - $rows = $result->fetchAll(); - - $this->assertEqual(count($rows), 1, new FormattableMarkup('Created an entry for %alias.', ['%alias' => $alias['alias']])); - - // Cache the pid for further tests. - $aliases[$idx]['pid'] = $rows[0]->id; - } - - // Load a few aliases - foreach ($aliases as $alias) { - $pid = $alias['pid']; - $loadedAlias = $aliasStorage->load(['pid' => $pid]); - $this->assertEqual($loadedAlias, $alias, new FormattableMarkup('Loaded the expected path with pid %pid.', ['%pid' => $pid])); - } - - // Load alias by source path. - $loadedAlias = $aliasStorage->load(['source' => '/node/1']); - $this->assertEqual($loadedAlias['alias'], '/alias_for_node_1_und', 'The last created alias loaded by default.'); - - // Update a few aliases - foreach ($aliases as $alias) { - $fields = $aliasStorage->save($alias['source'], $alias['alias'] . '_updated', $alias['langcode'], $alias['pid']); - - $this->assertEqual($alias['alias'], $fields['original']['alias']); - - $result = $connection->query('SELECT id FROM {path_alias} WHERE path = :path AND alias= :alias AND langcode = :langcode', [':path' => $alias['source'], ':alias' => $alias['alias'] . '_updated', ':langcode' => $alias['langcode']]); - $pid = $result->fetchField(); - - $this->assertEqual($pid, $alias['pid'], new FormattableMarkup('Updated entry for pid %pid.', ['%pid' => $pid])); - } - - // Delete a few aliases - foreach ($aliases as $alias) { - $pid = $alias['pid']; - $aliasStorage->delete(['pid' => $pid]); - - $result = $connection->query('SELECT * FROM {path_alias} WHERE id = :id', [':id' => $pid]); - $rows = $result->fetchAll(); - - $this->assertEqual(count($rows), 0, new FormattableMarkup('Deleted entry with pid %pid.', ['%pid' => $pid])); - } + $path_alias_repository = $this->container->get('path.alias_repository'); + $this->assertEquals('/test-alias', $path_alias_repository->lookupBySystemPath('/test-source-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['alias']); + $this->assertEquals('/test-alias', $path_alias_repository->lookupBySystemPath('/test-source-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['alias']); } /** - * Returns an array of URL aliases for testing. - * - * @return array of URL alias definitions. + * @covers ::lookupByAlias */ - protected function sampleUrlAliases() { - return [ - [ - 'source' => '/node/1', - 'alias' => '/alias_for_node_1_en', - 'langcode' => 'en', - ], - [ - 'source' => '/node/2', - 'alias' => '/alias_for_node_2_en', - 'langcode' => 'en', - ], - [ - 'source' => '/node/1', - 'alias' => '/alias_for_node_1_fr', - 'langcode' => 'fr', - ], - [ - 'source' => '/node/1', - 'alias' => '/alias_for_node_1_und', - 'langcode' => 'und', - ], - ]; + public function testLookupByAlias() { + $this->createPathAlias('/test-source', '/test-alias-Case'); + + $path_alias_repository = $this->container->get('path.alias_repository'); + $this->assertEquals('/test-source', $path_alias_repository->lookupByAlias('/test-alias-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['path']); + $this->assertEquals('/test-source', $path_alias_repository->lookupByAlias('/test-alias-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)['path']); } + /** + * @covers \Drupal\Core\Path\AliasManager::getPathByAlias + * @covers \Drupal\Core\Path\AliasManager::getAliasByPath + */ public function testLookupPath() { // Create AliasManager and Path object. $aliasManager = $this->container->get('path.alias_manager'); - $aliasStorage = $this->container->get('path.alias_storage'); // Test the situation where the source is the same for multiple aliases. // Start with a language-neutral alias, which we will override. - $path = [ - 'source' => "/user/1", - 'alias' => '/foo', - ]; - - $aliasStorage->save($path['source'], $path['alias']); - $this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'Basic alias lookup works.'); - $this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'Basic source lookup works.'); + $path_alias = $this->createPathAlias('/user/1', '/foo'); + $this->assertEquals($path_alias->getAlias(), $aliasManager->getAliasByPath($path_alias->getPath()), 'Basic alias lookup works.'); + $this->assertEquals($path_alias->getPath(), $aliasManager->getPathByAlias($path_alias->getAlias()), 'Basic source lookup works.'); // Create a language specific alias for the default language (English). - $path = [ - 'source' => "/user/1", - 'alias' => "/users/Dries", - 'langcode' => 'en', - ]; - $aliasStorage->save($path['source'], $path['alias'], $path['langcode']); - // Hook that clears cache is not executed with unit tests. - \Drupal::service('path.alias_manager')->cacheClear(); - $this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'English alias overrides language-neutral alias.'); - $this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'English source overrides language-neutral source.'); + $path_alias = $this->createPathAlias('/user/1', '/users/Dries', 'en'); + + $this->assertEquals($path_alias->getAlias(), $aliasManager->getAliasByPath($path_alias->getPath()), 'English alias overrides language-neutral alias.'); + $this->assertEquals($path_alias->getPath(), $aliasManager->getPathByAlias($path_alias->getAlias()), 'English source overrides language-neutral source.'); // Create a language-neutral alias for the same path, again. - $path = [ - 'source' => "/user/1", - 'alias' => '/bar', - ]; - $aliasStorage->save($path['source'], $path['alias']); - $this->assertEqual($aliasManager->getAliasByPath($path['source']), "/users/Dries", 'English alias still returned after entering a language-neutral alias.'); + $path_alias = $this->createPathAlias('/user/1', '/bar'); + $this->assertEquals("/users/Dries", $aliasManager->getAliasByPath($path_alias->getPath()), 'English alias still returned after entering a language-neutral alias.'); // Create a language-specific (xx-lolspeak) alias for the same path. - $path = [ - 'source' => "/user/1", - 'alias' => '/LOL', - 'langcode' => 'xx-lolspeak', - ]; - $aliasStorage->save($path['source'], $path['alias'], $path['langcode']); - $this->assertEqual($aliasManager->getAliasByPath($path['source']), "/users/Dries", 'English alias still returned after entering a LOLspeak alias.'); + $path_alias = $this->createPathAlias('/user/1', '/LOL', 'xx-lolspeak'); + $this->assertEquals("/users/Dries", $aliasManager->getAliasByPath($path_alias->getPath()), 'English alias still returned after entering a LOLspeak alias.'); // The LOLspeak alias should be returned if we really want LOLspeak. - $this->assertEqual($aliasManager->getAliasByPath($path['source'], 'xx-lolspeak'), '/LOL', 'LOLspeak alias returned if we specify xx-lolspeak to the alias manager.'); + $this->assertEquals('/LOL', $aliasManager->getAliasByPath($path_alias->getPath(), 'xx-lolspeak'), 'LOLspeak alias returned if we specify xx-lolspeak to the alias manager.'); // Create a new alias for this path in English, which should override the // previous alias for "user/1". - $path = [ - 'source' => "/user/1", - 'alias' => '/users/my-new-path', - 'langcode' => 'en', - ]; - $aliasStorage->save($path['source'], $path['alias'], $path['langcode']); - // Hook that clears cache is not executed with unit tests. - $aliasManager->cacheClear(); - $this->assertEqual($aliasManager->getAliasByPath($path['source']), $path['alias'], 'Recently created English alias returned.'); - $this->assertEqual($aliasManager->getPathByAlias($path['alias']), $path['source'], 'Recently created English source returned.'); + $path_alias = $this->createPathAlias('/user/1', '/users/my-new-path', 'en'); + $this->assertEquals($path_alias->getAlias(), $aliasManager->getAliasByPath($path_alias->getPath()), 'Recently created English alias returned.'); + $this->assertEquals($path_alias->getPath(), $aliasManager->getPathByAlias($path_alias->getAlias()), 'Recently created English source returned.'); // Remove the English aliases, which should cause a fallback to the most // recently created language-neutral alias, 'bar'. - $aliasStorage->delete(['langcode' => 'en']); - // Hook that clears cache is not executed with unit tests. - $aliasManager->cacheClear(); - $this->assertEqual($aliasManager->getAliasByPath($path['source']), '/bar', 'Path lookup falls back to recently created language-neutral alias.'); + $path_alias_storage = $this->container->get('entity_type.manager')->getStorage('path_alias'); + $entities = $path_alias_storage->loadByProperties(['langcode' => 'en']); + $path_alias_storage->delete($entities); + $this->assertEquals('/bar', $aliasManager->getAliasByPath($path_alias->getPath()), 'Path lookup falls back to recently created language-neutral alias.'); // Test the situation where the alias and language are the same, but // the source differs. The newer alias record should be returned. - $aliasStorage->save('/user/2', '/bar'); - // Hook that clears cache is not executed with unit tests. + $this->createPathAlias('/user/2', '/bar'); $aliasManager->cacheClear(); - $this->assertEqual($aliasManager->getPathByAlias('/bar'), '/user/2', 'Newer alias record is returned when comparing two LanguageInterface::LANGCODE_NOT_SPECIFIED paths with the same alias.'); + $this->assertEquals('/user/2', $aliasManager->getPathByAlias('/bar'), 'Newer alias record is returned when comparing two LanguageInterface::LANGCODE_NOT_SPECIFIED paths with the same alias.'); } /** @@ -198,9 +112,8 @@ class AliasTest extends KernelTestBase { $memoryCounterBackend = new MemoryCounterBackend(); // Create AliasManager and Path object. - $aliasStorage = $this->container->get('path.alias_storage'); - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $aliasStorage); - $aliasManager = new AliasManager($aliasStorage, $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); + $aliasManager = new AliasManager($this->container->get('path.alias_repository'), $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); // No alias for user and admin yet, so should be NULL. $this->assertNull($whitelist->get('user')); @@ -211,21 +124,23 @@ class AliasTest extends KernelTestBase { $this->assertNull($whitelist->get($this->randomMachineName())); // Add an alias for user/1, user should get whitelisted now. - $aliasStorage->save('/user/1', '/' . $this->randomMachineName()); + $this->createPathAlias('/user/1', '/' . $this->randomMachineName()); $aliasManager->cacheClear(); $this->assertTrue($whitelist->get('user')); $this->assertNull($whitelist->get('admin')); $this->assertNull($whitelist->get($this->randomMachineName())); // Add an alias for admin, both should get whitelisted now. - $aliasStorage->save('/admin/something', '/' . $this->randomMachineName()); + $this->createPathAlias('/admin/something', '/' . $this->randomMachineName()); $aliasManager->cacheClear(); $this->assertTrue($whitelist->get('user')); $this->assertTrue($whitelist->get('admin')); $this->assertNull($whitelist->get($this->randomMachineName())); // Remove the user alias again, whitelist entry should be removed. - $aliasStorage->delete(['source' => '/user/1']); + $path_alias_storage = $this->container->get('entity_type.manager')->getStorage('path_alias'); + $entities = $path_alias_storage->loadByProperties(['path' => '/user/1']); + $path_alias_storage->delete($entities); $aliasManager->cacheClear(); $this->assertNull($whitelist->get('user')); $this->assertTrue($whitelist->get('admin')); @@ -238,7 +153,7 @@ class AliasTest extends KernelTestBase { // Re-initialize the whitelist using the same cache backend, should load // from cache. - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $aliasStorage); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); $this->assertNull($whitelist->get('user')); $this->assertTrue($whitelist->get('admin')); $this->assertNull($whitelist->get($this->randomMachineName())); @@ -258,17 +173,15 @@ class AliasTest extends KernelTestBase { $memoryCounterBackend = new MemoryCounterBackend(); // Create AliasManager and Path object. - $aliasStorage = $this->container->get('path.alias_storage'); - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $aliasStorage); - $aliasManager = new AliasManager($aliasStorage, $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); + $aliasManager = new AliasManager($this->container->get('path.alias_repository'), $whitelist, $this->container->get('language_manager'), $memoryCounterBackend); // Whitelist cache should not exist at all yet. $this->assertFalse($memoryCounterBackend->get('path_alias_whitelist')); // Add some aliases for both menu routes we have. - $aliasStorage->save('/admin/something', '/' . $this->randomMachineName()); - $aliasStorage->save('/user/something', '/' . $this->randomMachineName()); - $aliasManager->cacheClear(); + $this->createPathAlias('/admin/something', '/' . $this->randomMachineName()); + $this->createPathAlias('/user/something', '/' . $this->randomMachineName()); // Lookup admin path in whitelist. It will query the DB and figure out // that it indeed has an alias, and add it to the internal whitelist and @@ -288,7 +201,7 @@ class AliasTest extends KernelTestBase { // Whitelist should load data from its cache, see that it hasn't done a // check for 'user' yet, perform the check, then mark the result to be // persisted to cache. - $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $aliasStorage); + $whitelist = new AliasWhitelist('path_alias_whitelist', $memoryCounterBackend, $this->container->get('lock'), $this->container->get('state'), $this->container->get('path.alias_repository')); $this->assertTrue($whitelist->get('user')); // Delete the whitelist cache. This could happen from an outside process, diff --git a/core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php b/core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php new file mode 100644 index 00000000000..dc15422796f --- /dev/null +++ b/core/tests/Drupal/KernelTests/Core/Path/LegacyAliasStorageTest.php @@ -0,0 +1,238 @@ +installEntitySchema('path_alias'); + $this->storage = $this->container->get('path.alias_storage'); + } + + /** + * @covers ::load + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testLoad() { + $this->storage->save('/test-source-Case', '/test-alias-Case'); + + $expected = [ + 'pid' => 1, + 'alias' => '/test-alias-Case', + 'source' => '/test-source-Case', + 'langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED, + ]; + + $this->assertEquals($expected, $this->storage->load(['alias' => '/test-alias-Case'])); + $this->assertEquals($expected, $this->storage->load(['alias' => '/test-alias-case'])); + $this->assertEquals($expected, $this->storage->load(['source' => '/test-source-Case'])); + $this->assertEquals($expected, $this->storage->load(['source' => '/test-source-case'])); + } + + /** + * @covers ::load + * @covers ::save + * @covers ::delete + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testCRUD() { + $entity_storage = \Drupal::entityTypeManager()->getStorage('path_alias'); + $aliases = $this->sampleUrlAliases(); + + // Create a few aliases + foreach ($aliases as $idx => $alias) { + $this->storage->save($alias['source'], $alias['alias'], $alias['langcode']); + + $result = $entity_storage->getQuery() + ->condition('path', $alias['source']) + ->condition('alias', $alias['alias']) + ->condition('langcode', $alias['langcode']) + ->execute(); + + $this->assertCount(1, $result, "Created an entry for {$alias['alias']}."); + + // Cache the pid for further tests. + $aliases[$idx]['pid'] = reset($result); + } + + // Load a few aliases + foreach ($aliases as $alias) { + $pid = $alias['pid']; + $loadedAlias = $this->storage->load(['pid' => $pid]); + $this->assertEquals($alias, $loadedAlias, "Loaded the expected path with pid $pid."); + } + + // Load alias by source path. + $loadedAlias = $this->storage->load(['source' => '/node/1']); + $this->assertEquals('/alias_for_node_1_und', $loadedAlias['alias'], 'The last created alias loaded by default.'); + + // Update a few aliases + foreach ($aliases as $alias) { + $fields = $this->storage->save($alias['source'], $alias['alias'] . '_updated', $alias['langcode'], $alias['pid']); + + $this->assertEquals($alias['alias'], $fields['original']['alias']); + + $result = $entity_storage->getQuery() + ->condition('path', $alias['source']) + ->condition('alias', $alias['alias'] . '_updated') + ->condition('langcode', $alias['langcode']) + ->execute(); + $pid = reset($result); + + $this->assertEquals($alias['pid'], $pid, "Updated entry for pid $pid."); + } + + // Delete a few aliases + foreach ($aliases as $alias) { + $pid = $alias['pid']; + $this->storage->delete(['pid' => $pid]); + + $result = $entity_storage->getQuery()->condition('id', $pid)->execute(); + + $this->assertCount(0, $result, "Deleted entry with pid $pid."); + } + } + + /** + * Returns an array of URL aliases for testing. + * + * @return array of URL alias definitions. + */ + protected function sampleUrlAliases() { + return [ + [ + 'source' => '/node/1', + 'alias' => '/alias_for_node_1_en', + 'langcode' => 'en', + ], + [ + 'source' => '/node/2', + 'alias' => '/alias_for_node_2_en', + 'langcode' => 'en', + ], + [ + 'source' => '/node/1', + 'alias' => '/alias_for_node_1_fr', + 'langcode' => 'fr', + ], + [ + 'source' => '/node/1', + 'alias' => '/alias_for_node_1_und', + 'langcode' => 'und', + ], + ]; + } + + /** + * @covers ::preloadPathAlias + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testPreLoadPathAlias() { + $this->storage->save('/test-source-Case', '/test-alias'); + + $this->assertEquals(['/test-source-Case' => '/test-alias'], $this->storage->preloadPathAlias(['/test-source-Case'], LanguageInterface::LANGCODE_NOT_SPECIFIED)); + } + + /** + * @covers ::lookupPathAlias + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testLookupPathAlias() { + $this->storage->save('/test-source-Case', '/test-alias'); + + $this->assertEquals('/test-alias', $this->storage->lookupPathAlias('/test-source-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); + $this->assertEquals('/test-alias', $this->storage->lookupPathAlias('/test-source-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); + } + + /** + * @covers ::lookupPathSource + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testLookupPathSource() { + $this->storage->save('/test-source', '/test-alias-Case'); + + $this->assertEquals('/test-source', $this->storage->lookupPathSource('/test-alias-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); + $this->assertEquals('/test-source', $this->storage->lookupPathSource('/test-alias-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); + } + + /** + * @covers ::aliasExists + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testAliasExists() { + $this->storage->save('/test-source-Case', '/test-alias-Case'); + + $this->assertTrue($this->storage->aliasExists('/test-alias-Case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); + $this->assertTrue($this->storage->aliasExists('/test-alias-case', LanguageInterface::LANGCODE_NOT_SPECIFIED)); + } + + /** + * @covers ::languageAliasExists + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testLanguageAliasExists() { + $this->assertFalse($this->storage->languageAliasExists()); + + $this->storage->save('/test-source-Case', '/test-alias-Case', 'en'); + $this->assertTrue($this->storage->languageAliasExists()); + } + + /** + * @covers ::getAliasesForAdminListing + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testGetAliasesForAdminListing() { + $this->storage->save('/test-source-Case', '/test-alias-Case'); + $this->storage->save('/another-test', '/another-test-alias'); + + $expected_alias_1 = new \stdClass(); + $expected_alias_1->pid = '2'; + $expected_alias_1->source = '/another-test'; + $expected_alias_1->alias = '/another-test-alias'; + $expected_alias_1->langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED; + + $expected_alias_2 = new \stdClass(); + $expected_alias_2->pid = '1'; + $expected_alias_2->source = '/test-source-Case'; + $expected_alias_2->alias = '/test-alias-Case'; + $expected_alias_2->langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED; + + $header = [['field' => 'alias', 'sort' => 'asc']]; + $this->assertEquals([$expected_alias_1, $expected_alias_2], $this->storage->getAliasesForAdminListing($header)); + } + + /** + * @covers ::pathHasMatchingAlias + * @expectedDeprecation \Drupal\Core\Path\AliasStorage is deprecated in drupal:8.8.0 and is removed from drupal:9.0.0. Use the "path.alias_repository" service instead, or the entity storage handler for the "path_alias" entity type for CRUD methods. See https://www.drupal.org/node/3013865. + */ + public function testPathHasMatchingAlias() { + $this->storage->save('/test-source-Case', '/test-alias-Case'); + + $this->assertTrue($this->storage->pathHasMatchingAlias('/test')); + $this->assertFalse($this->storage->pathHasMatchingAlias('/another')); + } + +} diff --git a/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php b/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php index ea23b6f8e65..a393a1ceeb2 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php @@ -4,6 +4,7 @@ namespace Drupal\KernelTests\Core\Routing; use Drupal\Core\DependencyInjection\ContainerBuilder; use Drupal\KernelTests\KernelTestBase; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Response; @@ -14,6 +15,8 @@ use Symfony\Component\HttpFoundation\Response; */ class ContentNegotiationRoutingTest extends KernelTestBase { + use PathAliasTestTrait; + /** * {@inheritdoc} */ @@ -46,13 +49,11 @@ class ContentNegotiationRoutingTest extends KernelTestBase { * Tests the content negotiation aspect of routing. */ public function testContentRouting() { - /** @var \Drupal\Core\Path\AliasStorageInterface $path_alias_storage */ - $path_alias_storage = $this->container->get('path.alias_storage'); // Alias with extension pointing to no extension/constant content-type. - $path_alias_storage->save('/conneg/html', '/alias.html'); + $this->createPathAlias('/conneg/html', '/alias.html'); // Alias with extension pointing to dynamic extension/linked content-type. - $path_alias_storage->save('/conneg/html?_format=json', '/alias.json'); + $this->createPathAlias('/conneg/html?_format=json', '/alias.json'); $tests = [ // ['path', 'accept', 'content-type'], diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php index f439164c4e2..bb1380faa73 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php @@ -18,6 +18,7 @@ use Drupal\Core\State\State; use Drupal\KernelTests\KernelTestBase; use Drupal\language\Entity\ConfigurableLanguage; use Drupal\Tests\Core\Routing\RoutingFixtures; +use Drupal\Tests\Traits\Core\PathAliasTestTrait; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\Routing\Exception\ResourceNotFoundException; @@ -32,6 +33,8 @@ use Symfony\Component\Routing\RouteCollection; */ class RouteProviderTest extends KernelTestBase { + use PathAliasTestTrait; + /** * Modules to enable. */ @@ -572,9 +575,7 @@ class RouteProviderTest extends KernelTestBase { $this->assertEqual(2, count($cache->data['routes'])); // A path with a path alias. - /** @var \Drupal\Core\Path\AliasStorageInterface $path_storage */ - $path_storage = \Drupal::service('path.alias_storage'); - $path_storage->save('/path/add/one', '/path/add-one'); + $this->createPathAlias('/path/add/one', '/path/add-one'); /** @var \Drupal\Core\Path\AliasManagerInterface $alias_manager */ $alias_manager = \Drupal::service('path.alias_manager'); $alias_manager->cacheClear(); diff --git a/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php b/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php index 644af818d78..40d982c7291 100644 --- a/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php +++ b/core/tests/Drupal/Tests/Core/Path/AliasManagerTest.php @@ -5,11 +5,12 @@ namespace Drupal\Tests\Core\Path; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageInterface; use Drupal\Core\Path\AliasManager; +use Drupal\Core\Path\AliasRepositoryInterface; use Drupal\Tests\UnitTestCase; /** * @coversDefaultClass \Drupal\Core\Path\AliasManager - * @group Path + * @group path */ class AliasManagerTest extends UnitTestCase { @@ -27,6 +28,13 @@ class AliasManagerTest extends UnitTestCase { */ protected $aliasStorage; + /** + * Alias repository. + * + * @var \Drupal\Core\Path\AliasRepositoryInterface|\PHPUnit\Framework\MockObject\MockObject + */ + protected $aliasRepository; + /** * Alias whitelist. * @@ -68,12 +76,12 @@ class AliasManagerTest extends UnitTestCase { protected function setUp() { parent::setUp(); - $this->aliasStorage = $this->createMock('Drupal\Core\Path\AliasStorageInterface'); + $this->aliasRepository = $this->createMock(AliasRepositoryInterface::class); $this->aliasWhitelist = $this->createMock('Drupal\Core\Path\AliasWhitelistInterface'); $this->languageManager = $this->createMock('Drupal\Core\Language\LanguageManagerInterface'); $this->cache = $this->createMock('Drupal\Core\Cache\CacheBackendInterface'); - $this->aliasManager = new AliasManager($this->aliasStorage, $this->aliasWhitelist, $this->languageManager, $this->cache); + $this->aliasManager = new AliasManager($this->aliasRepository, $this->aliasWhitelist, $this->languageManager, $this->cache); } @@ -92,8 +100,8 @@ class AliasManagerTest extends UnitTestCase { ->with(LanguageInterface::TYPE_URL) ->will($this->returnValue($language)); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathSource') + $this->aliasRepository->expects($this->once()) + ->method('lookupByAlias') ->with($alias, $language->getId()) ->will($this->returnValue(NULL)); @@ -113,10 +121,10 @@ class AliasManagerTest extends UnitTestCase { $language = $this->setUpCurrentLanguage(); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathSource') + $this->aliasRepository->expects($this->once()) + ->method('lookupByAlias') ->with($alias, $language->getId()) - ->will($this->returnValue($path)); + ->will($this->returnValue(['path' => $path])); $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias)); // Call it twice to test the static cache. @@ -135,10 +143,10 @@ class AliasManagerTest extends UnitTestCase { $this->languageManager->expects($this->never()) ->method('getCurrentLanguage'); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathSource') + $this->aliasRepository->expects($this->once()) + ->method('lookupByAlias') ->with($alias, 'de') - ->will($this->returnValue($path)); + ->will($this->returnValue(['path' => $path])); $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, 'de')); // Call it twice to test the static cache. @@ -164,8 +172,8 @@ class AliasManagerTest extends UnitTestCase { // The whitelist returns FALSE for that path part, so the storage should // never be called. - $this->aliasStorage->expects($this->never()) - ->method('lookupPathAlias'); + $this->aliasRepository->expects($this->never()) + ->method('lookupBySystemPath'); $this->assertEquals($path, $this->aliasManager->getAliasByPath($path)); } @@ -189,8 +197,8 @@ class AliasManagerTest extends UnitTestCase { ->with($path_part1) ->will($this->returnValue(TRUE)); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathAlias') + $this->aliasRepository->expects($this->once()) + ->method('lookupBySystemPath') ->with($path, $language->getId()) ->will($this->returnValue(NULL)); @@ -227,10 +235,10 @@ class AliasManagerTest extends UnitTestCase { ->with($path_part1) ->will($this->returnValue(TRUE)); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathAlias') + $this->aliasRepository->expects($this->once()) + ->method('lookupBySystemPath') ->with($path, $language->getId()) - ->will($this->returnValue($alias)); + ->will($this->returnValue(['alias' => $alias])); $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path)); // Call it twice to test the static cache. @@ -272,14 +280,14 @@ class AliasManagerTest extends UnitTestCase { ->with($path_part1) ->will($this->returnValue(TRUE)); - $this->aliasStorage->expects($this->once()) + $this->aliasRepository->expects($this->once()) ->method('preloadPathAlias') ->with($cached_paths[$language->getId()], $language->getId()) ->will($this->returnValue([$path => $alias])); // LookupPathAlias should not be called. - $this->aliasStorage->expects($this->never()) - ->method('lookupPathAlias'); + $this->aliasRepository->expects($this->never()) + ->method('lookupBySystemPath'); $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path)); // Call it twice to test the static cache. @@ -322,12 +330,12 @@ class AliasManagerTest extends UnitTestCase { // The requested language is different than the cached, so this will // need to load. - $this->aliasStorage->expects($this->never()) + $this->aliasRepository->expects($this->never()) ->method('preloadPathAlias'); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathAlias') + $this->aliasRepository->expects($this->once()) + ->method('lookupBySystemPath') ->with($path, $language->getId()) - ->will($this->returnValue($alias)); + ->will($this->returnValue(['alias' => $alias])); $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path)); // Call it twice to test the static cache. @@ -369,14 +377,14 @@ class AliasManagerTest extends UnitTestCase { ->with($path_part1) ->will($this->returnValue(TRUE)); - $this->aliasStorage->expects($this->once()) + $this->aliasRepository->expects($this->once()) ->method('preloadPathAlias') ->with($cached_paths[$language->getId()], $language->getId()) ->will($this->returnValue([$cached_path => $cached_alias])); // LookupPathAlias() should not be called. - $this->aliasStorage->expects($this->never()) - ->method('lookupPathAlias'); + $this->aliasRepository->expects($this->never()) + ->method('lookupBySystemPath'); $this->assertEquals($path, $this->aliasManager->getAliasByPath($path)); // Call it twice to test the static cache. @@ -417,13 +425,13 @@ class AliasManagerTest extends UnitTestCase { ->with($path_part1) ->will($this->returnValue(TRUE)); - $this->aliasStorage->expects($this->once()) + $this->aliasRepository->expects($this->once()) ->method('preloadPathAlias') ->with($cached_paths[$language->getId()], $language->getId()) ->will($this->returnValue([$cached_path => $cached_alias])); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathAlias') + $this->aliasRepository->expects($this->once()) + ->method('lookupBySystemPath') ->with($path, $language->getId()) ->will($this->returnValue(NULL)); @@ -445,10 +453,10 @@ class AliasManagerTest extends UnitTestCase { $path = '/path'; $alias = '/alias'; $language = $this->setUpCurrentLanguage(); - $this->aliasStorage->expects($this->exactly(2)) - ->method('lookupPathAlias') + $this->aliasRepository->expects($this->exactly(2)) + ->method('lookupBySystemPath') ->with($path, $language->getId()) - ->willReturn($alias); + ->willReturn(['alias' => $alias]); $this->aliasWhitelist->expects($this->any()) ->method('get') ->willReturn(TRUE); @@ -457,9 +465,8 @@ class AliasManagerTest extends UnitTestCase { $this->assertEquals($alias, $this->aliasManager->getAliasByPath($path, $language->getId())); // Check that the cache is populated. - $original_storage = clone $this->aliasStorage; - $this->aliasStorage->expects($this->never()) - ->method('lookupPathSource'); + $this->aliasRepository->expects($this->never()) + ->method('lookupByAlias'); $this->assertEquals($path, $this->aliasManager->getPathByAlias($alias, $language->getId())); // Clear specific source. @@ -506,15 +513,15 @@ class AliasManagerTest extends UnitTestCase { ->with($path_part1) ->will($this->returnValue(TRUE)); - $this->aliasStorage->expects($this->once()) + $this->aliasRepository->expects($this->once()) ->method('preloadPathAlias') ->with($cached_paths[$language->getId()], $language->getId()) ->will($this->returnValue([$cached_path => $cached_alias])); - $this->aliasStorage->expects($this->once()) - ->method('lookupPathAlias') + $this->aliasRepository->expects($this->once()) + ->method('lookupBySystemPath') ->with($path, $language->getId()) - ->will($this->returnValue($new_alias)); + ->will($this->returnValue(['alias' => $new_alias])); $this->assertEquals($new_alias, $this->aliasManager->getAliasByPath($path)); // Call it twice to test the static cache. diff --git a/core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php b/core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php new file mode 100644 index 00000000000..2f12e4c399c --- /dev/null +++ b/core/tests/Drupal/Tests/Traits/Core/PathAliasTestTrait.php @@ -0,0 +1,112 @@ +getStorage('path_alias')->create([ + 'path' => $path, + 'alias' => $alias, + 'langcode' => $langcode, + ]); + $path_alias->save(); + + return $path_alias; + } + + /** + * Gets the first result from a 'load by properties' storage call. + * + * @param array $conditions + * An array of query conditions. + * + * @return \Drupal\Core\Path\PathAliasInterface|null + * A path alias entity or NULL. + */ + protected function loadPathAliasByConditions($conditions) { + $storage = \Drupal::entityTypeManager()->getStorage('path_alias'); + $query = $storage->getQuery(); + foreach ($conditions as $field => $value) { + $query->condition($field, $value); + } + $entities = $storage->loadMultiple($query->execute()); + + return $entities ? reset($entities) : NULL; + } + + /** + * Asserts that a path alias exists in the storage. + * + * @param string $alias + * The path alias. + * @param string|null $langcode + * (optional) The language code of the path alias. + * @param string|null $path + * (optional) The system path of the path alias. + * @param string|null $message + * (optional) A message to display with the assertion. + */ + protected function assertPathAliasExists($alias, $langcode = NULL, $path = NULL, $message = NULL) { + $query = \Drupal::entityTypeManager()->getStorage('path_alias')->getQuery(); + $query->condition('alias', $alias, '='); + if ($langcode) { + $query->condition('langcode', $langcode, '='); + } + if ($path) { + $query->condition('path', $path, '='); + } + $query->count(); + + $this->assertTrue((bool) $query->execute(), $message); + } + + /** + * Asserts that a path alias does not exist in the storage. + * + * @param string $alias + * The path alias. + * @param string|null $langcode + * (optional) The language code of the path alias. + * @param string|null $path + * (optional) The system path of the path alias. + * @param string|null $message + * (optional) A message to display with the assertion. + */ + protected function assertPathAliasNotExists($alias, $langcode = NULL, $path = NULL, $message = NULL) { + $query = \Drupal::entityTypeManager()->getStorage('path_alias')->getQuery(); + $query->condition('alias', $alias, '='); + if ($langcode) { + $query->condition('langcode', $langcode, '='); + } + if ($path) { + $query->condition('path', $path, '='); + } + $query->count(); + + $this->assertFalse((bool) $query->execute(), $message); + } + +}