Commit 696296bd authored by catch's avatar catch

Issue #2233595 by amateescu, marcingy, SpartyDan, visabhishek, Sharique,...

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

(cherry picked from commit 9d58bed457c3110a224d1c6c500da717e05754c2)
parent dac7c6de
......@@ -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']
......
......@@ -4,6 +4,7 @@
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 @@
*/
class AliasManager implements AliasManagerInterface, CacheDecoratorInterface {
use DeprecatedServicePropertyTrait;
/**
* {@inheritdoc}
*/
protected $deprecatedProperties = ['storage' => 'path.alias_storage'];
/**
* The alias storage service.
* The path alias repository.
*
* @var \Drupal\Core\Path\AliasStorageInterface
* @var \Drupal\Core\Path\AliasRepositoryInterface
*/
protected $storage;
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 @@ public function getPathByAlias($alias, $langcode = NULL) {
}
// 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 @@ public function getAliasByPath($path, $langcode = NULL) {
// 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 @@ public function getAliasByPath($path, $langcode = NULL) {
}
// 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
......
......@@ -4,8 +4,6 @@
/**
* Find an alias for a path and vice versa.
*
* @see \Drupal\Core\Path\AliasStorageInterface
*/
interface AliasManagerInterface {
......
<?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');
}
}
<?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);
}
......@@ -9,12 +9,21 @@
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 {
......
......@@ -6,6 +6,13 @@
/**
* 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 {
......
......@@ -4,6 +4,7 @@
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 @@
*/
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 @@ public function get($offset) {
* {@inheritdoc}
*/
public function resolveCacheMiss($root) {
$exists = $this->aliasStorage->pathHasMatchingAlias('/' . $root);
$exists = $this->pathAliasRepository->pathHasMatchingAlias('/' . $root);
$this->storage[$root] = $exists;
$this->persist($root);
if ($exists) {
......
......@@ -13,6 +13,7 @@
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 @@
*/
class LinkFieldTest extends BrowserTestBase {
use PathAliasTestTrait;
/**
* Modules to enable.
*
......@@ -100,7 +103,7 @@ public function testURLValidation() {
$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();
......
......@@ -5,14 +5,18 @@
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 @@ public function testPathLanguageConfiguration() {
$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');
......
......@@ -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 @@ public function register(ContainerBuilder $container) {
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 @@ public function testPathAliasChange() {
$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());
......
......@@ -4,7 +4,6 @@
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 @@ protected function computeValue() {
$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 @@ public function defaultAccess($operation = 'view', AccountInterface $account = N
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);
}
}
......@@ -87,15 +87,22 @@ public static function validateFormElement(array &$element, FormStateInterface $
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());
}
}
}
......
......@@ -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