Issue #2233595 by amateescu, marcingy, SpartyDan, visabhishek, Sharique, carletex, slashrsm, hanoii, plach, Berdir, xjm: Deprecate the custom path alias storage

merge-requests/2419/head
catch 2019-10-24 09:35:41 +01:00
parent bf51490e46
commit bdf48170cd
37 changed files with 965 additions and 507 deletions

View File

@ -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']

View File

@ -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

View File

@ -4,8 +4,6 @@ namespace Drupal\Core\Path;
/**
* Find an alias for a path and vice versa.
*
* @see \Drupal\Core\Path\AliasStorageInterface
*/
interface AliasManagerInterface {

View File

@ -0,0 +1,142 @@
<?php
namespace Drupal\Core\Path;
use Drupal\Core\Database\Connection;
use Drupal\Core\Database\Query\Condition;
use Drupal\Core\Database\Query\SelectInterface;
use Drupal\Core\Language\LanguageInterface;
/**
* Provides the default path alias lookup operations.
*/
class AliasRepository implements AliasRepositoryInterface {
/**
* The database connection.
*
* @var \Drupal\Core\Database\Connection
*/
protected $connection;
/**
* Constructs an AliasRepository object.
*
* @param \Drupal\Core\Database\Connection $connection
* A database connection for reading and writing path aliases.
*/
public function __construct(Connection $connection) {
$this->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');
}
}

View File

@ -0,0 +1,81 @@
<?php
namespace Drupal\Core\Path;
/**
* Provides an interface for path alias lookup operations.
*
* The path alias repository service is only used internally in order to
* optimize alias lookup queries needed in the critical path of each request.
* However, it is not marked as an internal service because alternative storage
* backends still need to override it if they provide a different storage class
* for the PathAlias entity type.
*
* Whenever you need to determine whether an alias exists for a system path, or
* whether a system path has an alias, the 'path.alias_manager' service should
* be used instead.
*/
interface AliasRepositoryInterface {
/**
* Pre-loads path alias information for a given list of system paths.
*
* @param array $preloaded
* System paths that need preloading of aliases.
* @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.
*
* @return string[]
* System paths (keys) to alias (values) mapping.
*/
public function preloadPathAlias($preloaded, $langcode);
/**
* Searches a path alias for a given Drupal system path.
*
* The default implementation performs case-insensitive matching on the
* 'path' and 'alias' strings.
*
* @param string $path
* The system path to investigate for corresponding path aliases.
* @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.
*
* @return array|null
* An array containing the 'id', 'path', 'alias' and 'langcode' properties
* of a path alias, or NULL if none was found.
*/
public function lookupBySystemPath($path, $langcode);
/**
* Searches a path alias for a given alias.
*
* The default implementation performs case-insensitive matching on the
* 'path' and 'alias' strings.
*
* @param string $alias
* The alias to investigate for corresponding system paths.
* @param string $langcode
* Language code to search the alias with. If there's no alias defined for
* that language it will search aliases without language.
*
* @return array|null
* An array containing the 'id', 'path', 'alias' and 'langcode' properties
* of a path alias, or NULL if none was found.
*/
public function lookupByAlias($alias, $langcode);
/**
* Check if any alias exists starting with $initial_substring.
*
* @param string $initial_substring
* Initial system path substring to test against.
*
* @return bool
* TRUE if any alias exists, FALSE otherwise.
*/
public function pathHasMatchingAlias($initial_substring);
}

View File

@ -9,12 +9,21 @@ use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Extension\ModuleHandlerInterface;
use Drupal\Core\Language\LanguageInterface;
@trigger_error('\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.', E_USER_DEPRECATED);
/**
* Provides a class for CRUD operations on path aliases.
*
* All queries perform case-insensitive matching on the 'source' and 'alias'
* fields, so the aliases '/test-alias' and '/test-Alias' are considered to be
* the same, and will both refer to the same internal system path.
*
* @deprecated \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
*/
class AliasStorage implements AliasStorageInterface {

View File

@ -6,6 +6,13 @@ use Drupal\Core\Language\LanguageInterface;
/**
* Provides a class for CRUD operations on path aliases.
*
* @deprecated \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
*/
interface AliasStorageInterface {

View File

@ -4,6 +4,7 @@ namespace Drupal\Core\Path;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\CacheCollector;
use Drupal\Core\DependencyInjection\DeprecatedServicePropertyTrait;
use Drupal\Core\State\StateInterface;
use Drupal\Core\Lock\LockBackendInterface;
@ -12,6 +13,13 @@ use Drupal\Core\Lock\LockBackendInterface;
*/
class AliasWhitelist extends CacheCollector implements AliasWhitelistInterface {
use DeprecatedServicePropertyTrait;
/**
* {@inheritdoc}
*/
protected $deprecatedProperties = ['aliasStorage' => '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) {

View File

@ -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();

View File

@ -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');

View File

@ -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());

View File

@ -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);
}
}

View File

@ -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());
}
}
}

View File

@ -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');

View File

@ -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.

View File

@ -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');
}

View File

@ -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.
*

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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());
}
}

View File

@ -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.

View File

@ -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',
];

View File

@ -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');

View File

@ -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();
}
}

View File

@ -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;
}
/**

View File

@ -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')]);
}
}

View File

@ -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", "<?php function mydistro_install() {\Drupal::service('path.alias_storage')->save('/user/1', '/myrootuser');}");
file_put_contents("$path/mydistro.install", "<?php function mydistro_install() {\Drupal::entityTypeManager()->getStorage('path_alias')->create(['path' => '/user/1', 'alias' => '/myrootuser'])->save();}");
}
/**

View File

@ -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

View File

@ -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');

View File

@ -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);

View File

@ -1,83 +0,0 @@
<?php
namespace Drupal\KernelTests\Core\Path;
use Drupal\Core\Language\LanguageInterface;
use Drupal\KernelTests\KernelTestBase;
/**
* @coversDefaultClass \Drupal\Core\Path\AliasStorage
* @group path
*/
class AliasStorageTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system'];
/**
* @var \Drupal\Core\Path\AliasStorage
*/
protected $storage;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->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));
}
}

View File

@ -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,

View File

@ -0,0 +1,238 @@
<?php
namespace Drupal\KernelTests\Core\Path;
use Drupal\Core\Language\LanguageInterface;
use Drupal\KernelTests\KernelTestBase;
/**
* @coversDefaultClass \Drupal\Core\Path\AliasStorage
* @group path
* @group legacy
*/
class LegacyAliasStorageTest extends KernelTestBase {
/**
* {@inheritdoc}
*/
public static $modules = ['system'];
/**
* @var \Drupal\Core\Path\AliasStorage
*/
protected $storage;
/**
* {@inheritdoc}
*/
protected function setUp() {
parent::setUp();
$this->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'));
}
}

View File

@ -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'],

View File

@ -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();

View File

@ -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.

View File

@ -0,0 +1,112 @@
<?php
namespace Drupal\Tests\Traits\Core;
use Drupal\Core\Language\LanguageInterface;
/**
* Provides methods to create and assert path_alias entities.
*
* This trait is meant to be used only by test classes.
*/
trait PathAliasTestTrait {
/**
* Creates a new path alias.
*
* @param string $path
* The system path.
* @param string $alias
* The alias for the system path.
* @param string $langcode
* (optional) A language code for the path alias. Defaults to
* \Drupal\Core\Language\LanguageInterface::LANGCODE_NOT_SPECIFIED.
*
* @return \Drupal\Core\Path\PathAliasInterface
* A path alias entity.
*/
protected function createPathAlias($path, $alias, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED) {
/** @var \Drupal\Core\Path\PathAliasInterface $path_alias */
$path_alias = \Drupal::entityTypeManager()->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);
}
}