diff --git a/core/core.services.yml b/core/core.services.yml index a573e2c1bb..56bccb01c2 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -944,7 +944,7 @@ services: - { name: event_subscriber } path.alias_storage: class: Drupal\Core\Path\AliasStorage - arguments: ['@database', '@module_handler'] + arguments: ['@entity_type.manager'] tags: - { name: backend_overridable } path.matcher: diff --git a/core/lib/Drupal/Core/Path/AliasStorage.php b/core/lib/Drupal/Core/Path/AliasStorage.php index a02bc3534d..6e69e122e6 100644 --- a/core/lib/Drupal/Core/Path/AliasStorage.php +++ b/core/lib/Drupal/Core/Path/AliasStorage.php @@ -2,12 +2,8 @@ namespace Drupal\Core\Path; -use Drupal\Core\Cache\Cache; -use Drupal\Core\Database\Connection; -use Drupal\Core\Database\SchemaObjectExistsException; -use Drupal\Core\Extension\ModuleHandlerInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\LanguageInterface; -use Drupal\Core\Database\Query\Condition; /** * Provides a class for CRUD operations on path aliases. @@ -19,42 +15,33 @@ class AliasStorage implements AliasStorageInterface { /** - * The table for the url_alias storage. - */ - const TABLE = 'url_alias'; - - /** - * The database connection. + * The entity type manager. * - * @var \Drupal\Core\Database\Connection + * @var \Drupal\Core\Entity\EntityTypeManagerInterface */ - protected $connection; + protected $entityTypeManager; /** - * The module handler. + * The storage handler for path_alias entities. * - * @var \Drupal\Core\Extension\ModuleHandlerInterface + * @var \Drupal\Core\Path\PathAliasStorageInterface */ - protected $moduleHandler; + protected $pathAliasEntityStorage; /** * Constructs a Path CRUD object. * - * @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. */ - public function __construct(Connection $connection, ModuleHandlerInterface $module_handler) { - $this->connection = $connection; - $this->moduleHandler = $module_handler; + public function __construct(EntityTypeManagerInterface $entity_type_manager) { + $this->entityTypeManager = $entity_type_manager; } /** * {@inheritdoc} */ public function save($source, $alias, $langcode = LanguageInterface::LANGCODE_NOT_SPECIFIED, $pid = NULL) { - if ($source[0] !== '/') { throw new \InvalidArgumentException(sprintf('Source path %s has to start with a slash.', $source)); } @@ -63,390 +50,170 @@ public function save($source, $alias, $langcode = LanguageInterface::LANGCODE_NO throw new \InvalidArgumentException(sprintf('Alias path %s has to start with a slash.', $alias)); } - $fields = [ - 'source' => $source, - 'alias' => $alias, - 'langcode' => $langcode, - ]; - - // Insert or update the alias. - if (empty($pid)) { - $try_again = FALSE; - try { - $query = $this->connection->insert(static::TABLE) - ->fields($fields); - $pid = $query->execute(); - } - catch (\Exception $e) { - // If there was an exception, try to create the table. - if (!$try_again = $this->ensureTableExists()) { - // If the exception happened for other reason than the missing table, - // propagate the exception. - throw $e; + if ($pid) { + /** @var \Drupal\Core\Path\PathAliasInterface $path_alias */ + $path_alias = $this->getPathAliasEntityStorage()->load($pid); + $original_values = [ + 'source' => $path_alias->getPath(), + 'alias' => $path_alias->getAlias(), + 'langcode' => $path_alias->language()->getId(), + ]; + + if ($path_alias->language()->getId() != $langcode) { + if ($path_alias->hasTranslation($langcode)) { + $path_alias = $path_alias->getTranslation($langcode); + } + else { + $path_alias = $path_alias->addTranslation($langcode); } } - // Now that the table has been created, try again if necessary. - if ($try_again) { - $query = $this->connection->insert(static::TABLE) - ->fields($fields); - $pid = $query->execute(); - } - - $fields['pid'] = $pid; - $operation = 'insert'; + $path_alias->setPath($source); + $path_alias->setAlias($alias); } else { - // Fetch the current values so that an update hook can identify what - // exactly changed. - try { - $original = $this->connection->query('SELECT source, alias, langcode FROM {url_alias} WHERE pid = :pid', [':pid' => $pid]) - ->fetchAssoc(); - } - catch (\Exception $e) { - $this->catchException($e); - $original = FALSE; - } - $fields['pid'] = $pid; - $query = $this->connection->update(static::TABLE) - ->fields($fields) - ->condition('pid', $pid); - $pid = $query->execute(); - $fields['original'] = $original; - $operation = 'update'; + $path_alias = $this->getPathAliasEntityStorage()->create([ + 'path' => $source, + 'alias' => $alias, + 'langcode' => $langcode, + ]); } - if ($pid) { - // @todo Switch to using an event for this instead of a hook. - $this->moduleHandler->invokeAll('path_' . $operation, [$fields]); - Cache::invalidateTags(['route_match']); - return $fields; + + $path_alias->save(); + + $path_alias_values = [ + 'pid' => $path_alias->id(), + 'source' => $path_alias->getPath(), + 'alias' => $path_alias->getAlias(), + 'langcode' => $path_alias->language()->getId(), + ]; + + if (isset($original_values)) { + $path_alias_values['original'] = $original_values; } - return FALSE; + + return $path_alias_values; } /** * {@inheritdoc} */ public function load($conditions) { - $select = $this->connection->select(static::TABLE); + $query = $this->getPathAliasEntityStorage()->getQuery(); foreach ($conditions as $field => $value) { - if ($field == 'source' || $field == 'alias') { - // Use LIKE for case-insensitive matching. - $select->condition($field, $this->connection->escapeLike($value), 'LIKE'); + if ($field === 'source') { + $field = 'path'; } - else { - $select->condition($field, $value); + if ($field === 'pid') { + $field = 'id'; } + + $query->condition($field, $value, '='); } - try { - return $select - ->fields(static::TABLE) - ->orderBy('pid', 'DESC') - ->range(0, 1) - ->execute() - ->fetchAssoc(); - } - catch (\Exception $e) { - $this->catchException($e); - return FALSE; + + $result = $query + ->sort('id', 'DESC') + ->range(0, 1) + ->execute(); + $entities = $this->getPathAliasEntityStorage()->loadMultiple($result); + + /** @var \Drupal\Core\Path\PathAliasInterface $path_alias */ + $path_alias = reset($entities); + if ($path_alias) { + return [ + 'pid' => $path_alias->id(), + 'source' => $path_alias->getPath(), + 'alias' => $path_alias->getAlias(), + 'langcode' => $path_alias->language()->getId(), + ]; } + + return FALSE; } /** * {@inheritdoc} */ public function delete($conditions) { - $path = $this->load($conditions); - $query = $this->connection->delete(static::TABLE); + $query = $this->getPathAliasEntityStorage()->getQuery(); foreach ($conditions as $field => $value) { - if ($field == 'source' || $field == 'alias') { - // Use LIKE for case-insensitive matching. - $query->condition($field, $this->connection->escapeLike($value), 'LIKE'); + if ($field === 'source') { + $field = 'path'; } - else { - $query->condition($field, $value); + if ($field === 'pid') { + $field = 'id'; } + + $query->condition($field, $value, '='); } - try { - $deleted = $query->execute(); - } - catch (\Exception $e) { - $this->catchException($e); - $deleted = FALSE; - } - // @todo Switch to using an event for this instead of a hook. - $this->moduleHandler->invokeAll('path_delete', [$path]); - Cache::invalidateTags(['route_match']); - return $deleted; + + $result = $query->execute(); + $entities = $this->getPathAliasEntityStorage()->loadMultiple($result); + $this->getPathAliasEntityStorage()->delete($entities); } /** * {@inheritdoc} */ public function preloadPathAlias($preloaded, $langcode) { - $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; - $select = $this->connection->select(static::TABLE) - ->fields(static::TABLE, ['source', 'alias']); - - if (!empty($preloaded)) { - $conditions = new Condition('OR'); - foreach ($preloaded as $preloaded_item) { - $conditions->condition('source', $this->connection->escapeLike($preloaded_item), 'LIKE'); - } - $select->condition($conditions); - } - - // 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. We also - // order by pid ASC so that fetchAllKeyed() returns the most recently - // created alias for each source. Subsequent queries using fetchField() must - // use pid DESC to have the same effect. - if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) { - array_pop($langcode_list); - } - elseif ($langcode < LanguageInterface::LANGCODE_NOT_SPECIFIED) { - $select->orderBy('langcode', 'ASC'); - } - else { - $select->orderBy('langcode', 'DESC'); - } - - $select->orderBy('pid', 'ASC'); - $select->condition('langcode', $langcode_list, 'IN'); - try { - return $select->execute()->fetchAllKeyed(); - } - catch (\Exception $e) { - $this->catchException($e); - return FALSE; - } + return $this->getPathAliasEntityStorage()->preloadPathAlias($preloaded, $langcode); } /** * {@inheritdoc} */ public function lookupPathAlias($path, $langcode) { - $source = $this->connection->escapeLike($path); - $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; - - // See the queries above. Use LIKE for case-insensitive matching. - $select = $this->connection->select(static::TABLE) - ->fields(static::TABLE, ['alias']) - ->condition('source', $source, 'LIKE'); - if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) { - array_pop($langcode_list); - } - elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) { - $select->orderBy('langcode', 'DESC'); - } - else { - $select->orderBy('langcode', 'ASC'); - } - - $select->orderBy('pid', 'DESC'); - $select->condition('langcode', $langcode_list, 'IN'); - try { - return $select->execute()->fetchField(); - } - catch (\Exception $e) { - $this->catchException($e); - return FALSE; - } + return $this->getPathAliasEntityStorage()->lookupPathAlias($path, $langcode); } /** * {@inheritdoc} */ public function lookupPathSource($path, $langcode) { - $alias = $this->connection->escapeLike($path); - $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; - - // See the queries above. Use LIKE for case-insensitive matching. - $select = $this->connection->select(static::TABLE) - ->fields(static::TABLE, ['source']) - ->condition('alias', $alias, 'LIKE'); - if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) { - array_pop($langcode_list); - } - elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) { - $select->orderBy('langcode', 'DESC'); - } - else { - $select->orderBy('langcode', 'ASC'); - } - - $select->orderBy('pid', 'DESC'); - $select->condition('langcode', $langcode_list, 'IN'); - try { - return $select->execute()->fetchField(); - } - catch (\Exception $e) { - $this->catchException($e); - return FALSE; - } + return $this->getPathAliasEntityStorage()->lookupPathSource($path, $langcode); } /** * {@inheritdoc} */ public function aliasExists($alias, $langcode, $source = NULL) { - // Use LIKE and NOT LIKE for case-insensitive matching. - $query = $this->connection->select(static::TABLE) - ->condition('alias', $this->connection->escapeLike($alias), 'LIKE') - ->condition('langcode', $langcode); - if (!empty($source)) { - $query->condition('source', $this->connection->escapeLike($source), 'NOT LIKE'); - } - $query->addExpression('1'); - $query->range(0, 1); - try { - return (bool) $query->execute()->fetchField(); - } - catch (\Exception $e) { - $this->catchException($e); - return FALSE; - } + return $this->getPathAliasEntityStorage()->aliasExists($alias, $langcode, $source); } /** * {@inheritdoc} */ public function languageAliasExists() { - try { - return (bool) $this->connection->queryRange('SELECT 1 FROM {url_alias} WHERE langcode <> :langcode', 0, 1, [':langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED])->fetchField(); - } - catch (\Exception $e) { - $this->catchException($e); - return FALSE; - } + return $this->getPathAliasEntityStorage()->languageAliasExists(); } /** * {@inheritdoc} */ public function getAliasesForAdminListing($header, $keys = NULL) { - $query = $this->connection->select(static::TABLE) - ->extend('Drupal\Core\Database\Query\PagerSelectExtender') - ->extend('Drupal\Core\Database\Query\TableSortExtender'); - if ($keys) { - // Replace wildcards with PDO wildcards. - $query->condition('alias', '%' . preg_replace('!\*+!', '%', $keys) . '%', 'LIKE'); - } - try { - return $query - ->fields(static::TABLE) - ->orderByHeader($header) - ->limit(50) - ->execute() - ->fetchAll(); - } - catch (\Exception $e) { - $this->catchException($e); - return []; - } + return $this->getPathAliasEntityStorage()->getAliasesForAdminListing($header, $keys); } /** * {@inheritdoc} */ public function pathHasMatchingAlias($initial_substring) { - $query = $this->connection->select(static::TABLE, 'u'); - $query->addExpression(1); - try { - return (bool) $query - ->condition('u.source', $this->connection->escapeLike($initial_substring) . '%', 'LIKE') - ->range(0, 1) - ->execute() - ->fetchField(); - } - catch (\Exception $e) { - $this->catchException($e); - return FALSE; - } - } - - /** - * Check if the table exists and create it if not. - */ - protected function ensureTableExists() { - try { - $database_schema = $this->connection->schema(); - if (!$database_schema->tableExists(static::TABLE)) { - $schema_definition = $this->schemaDefinition(); - $database_schema->createTable(static::TABLE, $schema_definition); - return TRUE; - } - } - // If another process has already created the table, attempting to recreate - // it will throw an exception. In this case just catch the exception and do - // nothing. - catch (SchemaObjectExistsException $e) { - return TRUE; - } - return FALSE; + return $this->getPathAliasEntityStorage()->pathHasMatchingAlias($initial_substring); } /** - * Act on an exception when url_alias might be stale. + * Returns the path alias entity storage handler. * - * If the table does not yet exist, that's fine, but if the table exists and - * yet the query failed, then the url_alias is stale and the exception needs - * to propagate. + * We can not store it in the constructor because that leads to a circular + * dependency in the service container. * - * @param $e - * The exception. - * - * @throws \Exception + * @return \Drupal\Core\Path\PathAliasStorageInterface + * The path alias entity storage. */ - protected function catchException(\Exception $e) { - if ($this->connection->schema()->tableExists(static::TABLE)) { - throw $e; + protected function getPathAliasEntityStorage() { + if (!$this->pathAliasEntityStorage) { + $this->pathAliasEntityStorage = $this->entityTypeManager->getStorage('path_alias'); } - } - - /** - * Defines the schema for the {url_alias} table. - * - * @internal - */ - public static function schemaDefinition() { - return [ - 'description' => 'A list of URL aliases for Drupal paths; a user may visit either the source or destination path.', - 'fields' => [ - 'pid' => [ - 'description' => 'A unique path alias identifier.', - 'type' => 'serial', - 'unsigned' => TRUE, - 'not null' => TRUE, - ], - 'source' => [ - 'description' => 'The Drupal path this alias is for; e.g. node/12.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ], - 'alias' => [ - 'description' => 'The alias for this path; e.g. title-of-the-story.', - 'type' => 'varchar', - 'length' => 255, - 'not null' => TRUE, - 'default' => '', - ], - 'langcode' => [ - 'description' => "The language code this alias is for; if 'und', the alias will be used for unknown languages. Each Drupal path can have an alias for each supported language.", - 'type' => 'varchar_ascii', - 'length' => 12, - 'not null' => TRUE, - 'default' => '', - ], - ], - 'primary key' => ['pid'], - 'indexes' => [ - 'alias_langcode_pid' => ['alias', 'langcode', 'pid'], - 'source_langcode_pid' => ['source', 'langcode', 'pid'], - ], - ]; + return $this->pathAliasEntityStorage; } } diff --git a/core/lib/Drupal/Core/Path/Entity/PathAlias.php b/core/lib/Drupal/Core/Path/Entity/PathAlias.php new file mode 100644 index 0000000000..7a8ba50261 --- /dev/null +++ b/core/lib/Drupal/Core/Path/Entity/PathAlias.php @@ -0,0 +1,106 @@ +setLabel(new TranslatableMarkup('Path')) + ->setDescription(new TranslatableMarkup('The path that this alias belongs to.')) + ->setRequired(TRUE) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE) + ->setDefaultValue(''); + + $fields['alias'] = BaseFieldDefinition::create('string') + ->setLabel(new TranslatableMarkup('Alias')) + ->setDescription(new TranslatableMarkup('The alias used with this path.')) + ->setRequired(TRUE) + ->setTranslatable(TRUE) + ->setRevisionable(TRUE) + ->setDefaultValue(''); + + return $fields; + } + + /** + * {@inheritdoc} + */ + public function getPath() { + return $this->get('path')->value; + } + + /** + * {@inheritdoc} + */ + public function setPath($path) { + $this->set('path', $path); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getAlias() { + return $this->get('alias')->value; + } + + /** + * {@inheritdoc} + */ + public function setAlias($alias) { + $this->set('alias', $alias); + return $this; + } + + /** + * {@inheritdoc} + */ + public function getCacheTagsToInvalidate() { + return ['route_match']; + } + +} diff --git a/core/lib/Drupal/Core/Path/PathAliasInterface.php b/core/lib/Drupal/Core/Path/PathAliasInterface.php new file mode 100644 index 0000000000..73d2b4569f --- /dev/null +++ b/core/lib/Drupal/Core/Path/PathAliasInterface.php @@ -0,0 +1,48 @@ + $entity->id(), + 'source' => $entity->getPath(), + 'alias' => $entity->getAlias(), + 'langcode' => $entity->language()->getId(), + ]; + + if ($hook === 'update') { + $values['original'] = [ + 'pid' => $entity->id(), + 'source' => $entity->original->getPath(), + 'alias' => $entity->original->getAlias(), + 'langcode' => $entity->original->language()->getId(), + ]; + } + + $this->moduleHandler()->invokeAll('path_' . $hook, [$values]); + } + } + + /** + * {@inheritdoc} + */ + public function preloadPathAlias($preloaded, $langcode) { + $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; + $select = $this->database->select($this->dataTable) + ->fields($this->dataTable, ['path', 'alias']); + + if (!empty($preloaded)) { + $conditions = new Condition('OR'); + foreach ($preloaded as $preloaded_item) { + $conditions->condition('path', $this->database->escapeLike($preloaded_item), 'LIKE'); + } + $select->condition($conditions); + } + + // 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. We also + // 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. + if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) { + array_pop($langcode_list); + } + elseif ($langcode < LanguageInterface::LANGCODE_NOT_SPECIFIED) { + $select->orderBy('langcode', 'ASC'); + } + else { + $select->orderBy('langcode', 'DESC'); + } + + $select->orderBy('id', 'ASC'); + $select->condition('langcode', $langcode_list, 'IN'); + + return $select->execute()->fetchAllKeyed(); + } + + /** + * {@inheritdoc} + */ + public function lookupPathAlias($path, $langcode) { + $path = $this->database->escapeLike($path); + $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; + + // See the queries above. Use LIKE for case-insensitive matching. + $select = $this->database->select($this->dataTable) + ->fields($this->dataTable, ['alias']) + ->condition('path', $path, 'LIKE'); + if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) { + array_pop($langcode_list); + } + elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) { + $select->orderBy('langcode', 'DESC'); + } + else { + $select->orderBy('langcode', 'ASC'); + } + + $select->orderBy('id', 'DESC'); + $select->condition('langcode', $langcode_list, 'IN'); + + return $select->execute()->fetchField(); + } + + /** + * {@inheritdoc} + */ + public function lookupPathSource($path, $langcode) { + $alias = $this->database->escapeLike($path); + $langcode_list = [$langcode, LanguageInterface::LANGCODE_NOT_SPECIFIED]; + + // See the queries above. Use LIKE for case-insensitive matching. + $select = $this->database->select($this->dataTable, 'pafd'); + $select->addField('pafd', 'path', 'source'); + $select->condition('alias', $alias, 'LIKE'); + if ($langcode == LanguageInterface::LANGCODE_NOT_SPECIFIED) { + array_pop($langcode_list); + } + elseif ($langcode > LanguageInterface::LANGCODE_NOT_SPECIFIED) { + $select->orderBy('langcode', 'DESC'); + } + else { + $select->orderBy('langcode', 'ASC'); + } + + $select->orderBy('id', 'DESC'); + $select->condition('langcode', $langcode_list, 'IN'); + + return $select->execute()->fetchField(); + } + + /** + * {@inheritdoc} + */ + public function aliasExists($alias, $langcode, $path = NULL) { + // Use LIKE and NOT LIKE for case-insensitive matching. + $query = $this->database->select($this->dataTable) + ->condition('alias', $this->database->escapeLike($alias), 'LIKE') + ->condition('langcode', $langcode); + if (!empty($path)) { + $query->condition('path', $this->database->escapeLike($path), 'NOT LIKE'); + } + $query->addExpression('1'); + $query->range(0, 1); + + return (bool) $query->execute()->fetchField(); + } + + /** + * {@inheritdoc} + */ + public function languageAliasExists() { + return (bool) $this->database->queryRange('SELECT 1 FROM {' . $this->dataTable . '} WHERE langcode <> :langcode', 0, 1, [':langcode' => LanguageInterface::LANGCODE_NOT_SPECIFIED])->fetchField(); + } + + /** + * {@inheritdoc} + */ + public function getAliasesForAdminListing($header, $keys = NULL) { + $query = $this->database->select($this->dataTable, 'pafd') + ->extend('Drupal\Core\Database\Query\PagerSelectExtender') + ->extend('Drupal\Core\Database\Query\TableSortExtender'); + if ($keys) { + // Replace wildcards with PDO wildcards. + $query->condition('alias', '%' . preg_replace('!\*+!', '%', $keys) . '%', 'LIKE'); + } + + $query->addField('pafd', 'id', 'pid'); + $query->addField('pafd', 'path', 'source'); + return $query + ->fields('pafd') + ->orderByHeader($header) + ->limit(50) + ->execute() + ->fetchAll(); + } + + /** + * {@inheritdoc} + */ + public function pathHasMatchingAlias($initial_substring) { + $query = $this->database->select($this->dataTable); + $query->addExpression(1); + + return (bool) $query + ->condition('path', $this->database->escapeLike($initial_substring) . '%', 'LIKE') + ->range(0, 1) + ->execute() + ->fetchField(); + } + +} diff --git a/core/lib/Drupal/Core/Path/PathAliasStorageInterface.php b/core/lib/Drupal/Core/Path/PathAliasStorageInterface.php new file mode 100644 index 0000000000..051256c445 --- /dev/null +++ b/core/lib/Drupal/Core/Path/PathAliasStorageInterface.php @@ -0,0 +1,111 @@ +storage->getDataTable()) { + $schema[$data_table]['indexes'] += [ + 'path_alias__alias_langcode_id' => ['alias', 'langcode', 'id'], + 'path_alias__path_langcode_id' => ['path', 'langcode', 'id'], + ]; + } + + return $schema; + } + +} diff --git a/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php b/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php index 6a7cf4a363..028d23af1c 100644 --- a/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php +++ b/core/modules/menu_link_content/tests/src/Kernel/PathAliasMenuLinkContentTest.php @@ -27,6 +27,7 @@ protected function setUp() { parent::setUp(); $this->installEntitySchema('menu_link_content'); + $this->installEntitySchema('path_alias'); // Ensure that the weight of module_link_content is higher than system. // @see menu_link_content_install() diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php index 26117ba61a..a69a96caf9 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d6/MigrateUpgrade6Test.php @@ -84,6 +84,7 @@ protected function getEntityCounts() { 'shortcut_set' => 1, 'action' => 23, 'menu' => 8, + 'path_alias' => 8, 'taxonomy_term' => 8, 'taxonomy_vocabulary' => 7, 'tour' => 5, diff --git a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php index 28fe7d0a5c..2a69b3c769 100644 --- a/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php +++ b/core/modules/migrate_drupal_ui/tests/src/Functional/d7/MigrateUpgrade7Test.php @@ -85,6 +85,7 @@ protected function getEntityCounts() { 'shortcut_set' => 2, 'action' => 17, 'menu' => 6, + 'path_alias' => 6, 'taxonomy_term' => 18, 'taxonomy_vocabulary' => 4, 'tour' => 4, diff --git a/core/modules/path/tests/src/Functional/PathAliasTest.php b/core/modules/path/tests/src/Functional/PathAliasTest.php index b8ac5968db..da1f4d5d18 100644 --- a/core/modules/path/tests/src/Functional/PathAliasTest.php +++ b/core/modules/path/tests/src/Functional/PathAliasTest.php @@ -339,7 +339,10 @@ public function testNodeAlias() { * Integer representing the path ID. */ public function getPID($alias) { - return db_query("SELECT pid FROM {url_alias} WHERE alias = :alias", [':alias' => $alias])->fetchField(); + $result = \Drupal::entityTypeManager()->getStorage('path_alias')->getQuery() + ->condition('alias', $alias, '=') + ->execute(); + return reset($result); } /** diff --git a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php index 28f22ecc95..2676844cdc 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d6/MigrateUrlAliasTest.php @@ -31,6 +31,7 @@ class MigrateUrlAliasTest extends MigrateDrupal6TestBase { protected function setUp() { parent::setUp(); $this->installEntitySchema('node'); + $this->installEntitySchema('path_alias'); $this->installConfig(['node']); $this->installSchema('node', ['node_access']); $this->migrateUsers(FALSE); diff --git a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php index b8d92e0c49..587709ce5d 100644 --- a/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php +++ b/core/modules/path/tests/src/Kernel/Migrate/d7/MigrateUrlAliasTest.php @@ -32,6 +32,7 @@ protected function setUp() { parent::setUp(); $this->installEntitySchema('node'); + $this->installEntitySchema('path_alias'); $this->installConfig('node'); $this->installSchema('node', ['node_access']); diff --git a/core/modules/path/tests/src/Kernel/PathItemTest.php b/core/modules/path/tests/src/Kernel/PathItemTest.php index 224a8420b3..f001dc6bcf 100644 --- a/core/modules/path/tests/src/Kernel/PathItemTest.php +++ b/core/modules/path/tests/src/Kernel/PathItemTest.php @@ -29,6 +29,7 @@ protected function setUp() { $this->installEntitySchema('node'); $this->installEntitySchema('user'); + $this->installEntitySchema('path_alias'); $this->installSchema('node', ['node_access']); diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php index 832cc82d68..62b0fee993 100644 --- a/core/modules/simpletest/src/KernelTestBase.php +++ b/core/modules/simpletest/src/KernelTestBase.php @@ -384,7 +384,7 @@ public function containerBuild(ContainerBuilder $container) { } if ($container->hasDefinition('path_processor_alias')) { - // Prevent the alias-based path processor, which requires a url_alias db + // Prevent the alias-based path processor, which requires a path_alias db // table, from being registered to the path processor manager. We do this // by removing the tags that the compiler pass looks for. This means the // url generator can safely be used within tests. diff --git a/core/modules/system/src/Tests/Path/UrlAliasFixtures.php b/core/modules/system/src/Tests/Path/UrlAliasFixtures.php deleted file mode 100644 index 41fd896dfe..0000000000 --- a/core/modules/system/src/Tests/Path/UrlAliasFixtures.php +++ /dev/null @@ -1,96 +0,0 @@ -tableDefinition(); - $schema = $connection->schema(); - - foreach ($tables as $name => $table) { - $schema->dropTable($name); - $schema->createTable($name, $table); - } - } - - /** - * Drop the tables used for the sample data. - * - * @param \Drupal\Core\Database\Connection $connection - * The connection to use to drop the tables. - */ - public function dropTables(Connection $connection) { - $tables = $this->tableDefinition(); - $schema = $connection->schema(); - - foreach ($tables as $name => $table) { - $schema->dropTable($name); - } - } - - /** - * Returns an array of URL aliases for testing. - * - * @return array of URL alias definitions. - */ - public 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', - ], - ]; - } - - /** - * Returns the table definition for the URL alias fixtures. - * - * @return array - * Table definitions. - */ - public function tableDefinition() { - $tables = []; - - // Prime the drupal_get_filename() cache with the location of the system - // module as its location is known and shouldn't change. - // @todo Remove as part of https://www.drupal.org/node/2186491 - drupal_get_filename('module', 'system', 'core/modules/system/system.info.yml'); - module_load_install('system'); - $schema = system_schema(); - - $tables['url_alias'] = AliasStorage::schemaDefinition(); - $tables['key_value'] = $schema['key_value']; - - return $tables; - } - -} diff --git a/core/modules/system/system.install b/core/modules/system/system.install index 6eb8c12005..a2f6a75a70 100644 --- a/core/modules/system/system.install +++ b/core/modules/system/system.install @@ -11,7 +11,6 @@ use Drupal\Component\Utility\OpCodeCache; use Drupal\Component\Utility\Unicode; use Drupal\Core\Cache\Cache; -use Drupal\Core\Path\AliasStorage; use Drupal\Core\Url; use Drupal\Core\Database\Database; use Drupal\Core\Entity\ContentEntityTypeInterface; @@ -1182,11 +1181,6 @@ function system_schema() { ], ]; - // Create the url_alias table. The alias_storage service can auto-create its - // table, but this relies on exceptions being thrown. These exceptions will be - // thrown every request until an alias is created. - $schema['url_alias'] = AliasStorage::schemaDefinition(); - return $schema; } diff --git a/core/modules/system/system.post_update.php b/core/modules/system/system.post_update.php index 0662853339..4e26d6e3e0 100644 --- a/core/modules/system/system.post_update.php +++ b/core/modules/system/system.post_update.php @@ -6,10 +6,13 @@ */ use Drupal\Core\Config\Entity\ConfigEntityUpdater; +use Drupal\Core\Entity\ContentEntityType; use Drupal\Core\Entity\Display\EntityDisplayInterface; use Drupal\Core\Entity\Display\EntityViewDisplayInterface; use Drupal\Core\Entity\Entity\EntityFormDisplay; use Drupal\Core\Entity\Entity\EntityViewDisplay; +use Drupal\Core\Site\Settings; +use Drupal\Core\StringTranslation\TranslatableMarkup; /** * Re-save all configuration entities to recalculate dependencies. @@ -169,3 +172,74 @@ function system_post_update_extra_fields(&$sandbox = NULL) { $config_entity_updater->update($sandbox, 'entity_form_display', $callback); $config_entity_updater->update($sandbox, 'entity_view_display', $callback); } + +/** + * Convert path aliases to entities. + */ +function system_post_update_convert_path_aliases_to_entities(&$sandbox = NULL) { + $database = \Drupal::database(); + + if (!isset($sandbox['current'])) { + // This must be the first run. Initialize the sandbox. + $sandbox['progress'] = 0; + $sandbox['current'] = 0; + + $entity_type = new ContentEntityType([ + 'id' => 'path_alias', + 'class' => '\Drupal\Core\Path\Entity\PathAlias', + 'label' => new TranslatableMarkup('Path alias'), + 'handlers' => [ + 'storage' => 'Drupal\Core\Path\PathAliasStorage', + 'storage_schema' => 'Drupal\Core\Path\PathAliasStorageSchema', + 'translation' => 'Drupal\content_translation\ContentTranslationHandler', + ], + 'base_table' => 'path_alias', + 'data_table' => 'path_alias_field_data', + 'revision_table' => 'path_alias_revision', + 'revision_data_table' => 'path_alias_field_revision', + 'translatable' => TRUE, + 'entity_keys' => [ + 'id' => 'id', + 'revision' => 'revision_id', + 'langcode' => 'langcode', + 'uuid' => 'uuid', + ], + ]); + \Drupal::entityDefinitionUpdateManager()->installEntityType($entity_type); + } + + $step_size = Settings::get('entity_update_batch_size', 50); + $url_aliases = $database->select('url_alias', 'ua') + ->condition("ua.pid", $sandbox['current'], '>') + ->fields('ua') + ->orderBy('pid', 'ASC') + ->range(0, $step_size) + ->execute() + ->fetchAll(); + + $storage = \Drupal::entityTypeManager()->getStorage('path_alias'); + foreach ($url_aliases as $url_alias) { + $path_alias = $storage->create([ + 'id' => $url_alias->pid, + 'path' => $url_alias->source, + 'alias' => $url_alias->alias, + 'langcode' => $url_alias->langcode, + ]); + $path_alias->enforceIsNew(TRUE); + $path_alias->save(); + + $sandbox['progress']++; + $sandbox['current'] = $url_alias->pid; + } + + // If we're not in maintenance mode, the number of path aliasa could change at + // any time so make sure that we always use the latest record count. + $sandbox['max'] = (int) $database->select('url_alias', 't')->countQuery()->execute()->fetchField(); + $sandbox['#finished'] = empty($sandbox['max']) ? 1 : ($sandbox['progress'] / $sandbox['max']); + + if ($sandbox['#finished'] >= 1) { + $database->schema()->dropTable('url_alias'); + + return t('Path aliases have been converted to entities.'); + } +} diff --git a/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php b/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php index afd697a582..6e22a4adbf 100644 --- a/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php +++ b/core/modules/system/tests/src/Functional/Path/UrlAlterFunctionalTest.php @@ -25,8 +25,8 @@ class UrlAlterFunctionalTest extends BrowserTestBase { * Test that URL altering works and that it occurs in the correct order. */ public function testUrlAlter() { - // Ensure that the url_alias table exists after Drupal installation. - $this->assertTrue(Database::getConnection()->schema()->tableExists('url_alias'), 'The url_alias table exists after Drupal installation.'); + // Ensure that the path_alias table exists after Drupal installation. + $this->assertTrue(Database::getConnection()->schema()->tableExists('path_alias'), 'The path_alias table exists after Drupal installation.'); // User names can have quotes and plus signs so we should ensure that URL // altering works with this. diff --git a/core/modules/system/tests/src/Kernel/PathHooksTest.php b/core/modules/system/tests/src/Kernel/PathHooksTest.php index fcbb499979..afa6176027 100644 --- a/core/modules/system/tests/src/Kernel/PathHooksTest.php +++ b/core/modules/system/tests/src/Kernel/PathHooksTest.php @@ -17,6 +17,15 @@ class PathHooksTest extends KernelTestBase { */ static public $modules = ['system']; + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->installEntitySchema('path_alias'); + } + /** * Test system_path_*() correctly clears caches. */ diff --git a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php index 16070c0e2e..7d7148febc 100644 --- a/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php +++ b/core/tests/Drupal/KernelTests/Core/Command/DbDumpTest.php @@ -93,6 +93,7 @@ protected function setUp() { $this->installEntitySchema('user'); $this->installEntitySchema('file'); $this->installEntitySchema('menu_link_content'); + $this->installEntitySchema('path_alias'); $this->installSchema('system', 'sequences'); // Place some sample config to test for in the export. @@ -107,7 +108,7 @@ protected function setUp() { $account = User::create(['mail' => 'q\'uote$dollar@example.com', 'name' => '$dollar']); $account->save(); - // Create url_alias (this will create 'url_alias'). + // Create a path alias. $this->container->get('path.alias_storage')->save('/user/' . $account->id(), '/user/example'); // Create a cache table (this will create 'cache_discovery'). @@ -132,7 +133,10 @@ protected function setUp() { 'menu_link_content_data', 'sequences', 'sessions', - 'url_alias', + 'path_alias', + 'path_alias_field_data', + 'path_alias_revision', + 'path_alias_field_revision', 'user__roles', 'users', 'users_field_data', diff --git a/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php b/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php index d7b24b497f..2e986d18b3 100644 --- a/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php +++ b/core/tests/Drupal/KernelTests/Core/Path/AliasStorageTest.php @@ -27,6 +27,7 @@ class AliasStorageTest extends KernelTestBase { protected function setUp() { parent::setUp(); + $this->installEntitySchema('path_alias'); $this->storage = $this->container->get('path.alias_storage'); } diff --git a/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php b/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php index 48d6b7d985..1cccc56827 100644 --- a/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php +++ b/core/tests/Drupal/KernelTests/Core/Path/AliasTest.php @@ -7,35 +7,48 @@ use Drupal\Core\Database\Database; use Drupal\Core\Path\AliasManager; use Drupal\Core\Path\AliasWhitelist; +use Drupal\KernelTests\KernelTestBase; /** * Tests path alias CRUD and lookup functionality. * * @group Path */ -class AliasTest extends PathUnitTestBase { +class AliasTest extends KernelTestBase { + + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + // The alias whitelist expects that the menu path roots are set by a + // menu router rebuild. + \Drupal::state()->set('router.path_roots', ['user', 'admin']); + + $this->installEntitySchema('path_alias'); + } public function testCRUD() { // Prepare database table. $connection = Database::getConnection(); - $this->fixtures->createTables($connection); // Create Path object. - $aliasStorage = new AliasStorage($connection, $this->container->get('module_handler')); + $aliasStorage = new AliasStorage($this->container->get('entity_type.manager')); - $aliases = $this->fixtures->sampleUrlAliases(); + $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 {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', [':source' => $alias['source'], ':alias' => $alias['alias'], ':langcode' => $alias['langcode']]); + $result = $connection->query('SELECT * FROM {path_alias_field_data} 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, format_string('Created an entry for %alias.', ['%alias' => $alias['alias']])); // Cache the pid for further tests. - $aliases[$idx]['pid'] = $rows[0]->pid; + $aliases[$idx]['pid'] = $rows[0]->id; } // Load a few aliases @@ -55,7 +68,7 @@ public function testCRUD() { $this->assertEqual($alias['alias'], $fields['original']['alias']); - $result = $connection->query('SELECT pid FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', [':source' => $alias['source'], ':alias' => $alias['alias'] . '_updated', ':langcode' => $alias['langcode']]); + $result = $connection->query('SELECT id FROM {path_alias_field_data} 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'], format_string('Updated entry for pid %pid.', ['%pid' => $pid])); @@ -66,21 +79,47 @@ public function testCRUD() { $pid = $alias['pid']; $aliasStorage->delete(['pid' => $pid]); - $result = $connection->query('SELECT * FROM {url_alias} WHERE pid = :pid', [':pid' => $pid]); + $result = $connection->query('SELECT * FROM {path_alias_field_data} WHERE id = :id', [':id' => $pid]); $rows = $result->fetchAll(); $this->assertEqual(count($rows), 0, format_string('Deleted entry with pid %pid.', ['%pid' => $pid])); } } - public function testLookupPath() { - // Prepare database table. - $connection = Database::getConnection(); - $this->fixtures->createTables($connection); + /** + * 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', + ], + ]; + } + public function testLookupPath() { // Create AliasManager and Path object. $aliasManager = $this->container->get('path.alias_manager'); - $aliasStorage = new AliasStorage($connection, $this->container->get('module_handler')); + $aliasStorage = new AliasStorage($this->container->get('entity_type.manager')); // Test the situation where the source is the same for multiple aliases. // Start with a language-neutral alias, which we will override. @@ -156,14 +195,10 @@ public function testLookupPath() { * Tests the alias whitelist. */ public function testWhitelist() { - // Prepare database table. - $connection = Database::getConnection(); - $this->fixtures->createTables($connection); - $memoryCounterBackend = new MemoryCounterBackend(); // Create AliasManager and Path object. - $aliasStorage = new AliasStorage($connection, $this->container->get('module_handler')); + $aliasStorage = new AliasStorage($this->container->get('entity_type.manager')); $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); @@ -220,14 +255,10 @@ public function testWhitelist() { * Tests situation where the whitelist cache is deleted mid-request. */ public function testWhitelistCacheDeletionMidRequest() { - // Prepare database table. - $connection = Database::getConnection(); - $this->fixtures->createTables($connection); - $memoryCounterBackend = new MemoryCounterBackend(); // Create AliasManager and Path object. - $aliasStorage = new AliasStorage($connection, $this->container->get('module_handler')); + $aliasStorage = new AliasStorage($this->container->get('entity_type.manager')); $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); diff --git a/core/tests/Drupal/KernelTests/Core/Path/PathUnitTestBase.php b/core/tests/Drupal/KernelTests/Core/Path/PathUnitTestBase.php deleted file mode 100644 index 671dbc4792..0000000000 --- a/core/tests/Drupal/KernelTests/Core/Path/PathUnitTestBase.php +++ /dev/null @@ -1,33 +0,0 @@ -fixtures = new UrlAliasFixtures(); - // The alias whitelist expects that the menu path roots are set by a - // menu router rebuild. - \Drupal::state()->set('router.path_roots', ['user', 'admin']); - } - - protected function tearDown() { - $this->fixtures->dropTables(Database::getConnection()); - - parent::tearDown(); - } - -} diff --git a/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php b/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php index 34c6d5852a..ea23b6f8e6 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/ContentNegotiationRoutingTest.php @@ -19,6 +19,15 @@ class ContentNegotiationRoutingTest extends KernelTestBase { */ public static $modules = ['conneg_test']; + /** + * {@inheritdoc} + */ + protected function setUp() { + parent::setUp(); + + $this->installEntitySchema('path_alias'); + } + /** * {@inheritdoc} */ diff --git a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php index 5f23e0b605..e1df2d8cb1 100644 --- a/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php +++ b/core/tests/Drupal/KernelTests/Core/Routing/RouteProviderTest.php @@ -89,6 +89,7 @@ protected function setUp() { $this->cache = new MemoryBackend(); $this->pathProcessor = \Drupal::service('path_processor_manager'); $this->cacheTagsInvalidator = \Drupal::service('cache_tags.invalidator'); + $this->installEntitySchema('path_alias'); } /** diff --git a/core/tests/Drupal/KernelTests/KernelTestBase.php b/core/tests/Drupal/KernelTests/KernelTestBase.php index 7d4a92d6b7..4025642553 100644 --- a/core/tests/Drupal/KernelTests/KernelTestBase.php +++ b/core/tests/Drupal/KernelTests/KernelTestBase.php @@ -558,7 +558,7 @@ public function register(ContainerBuilder $container) { } if ($container->hasDefinition('path_processor_alias')) { - // Prevent the alias-based path processor, which requires a url_alias db + // Prevent the alias-based path processor, which requires a path_alias db // table, from being registered to the path processor manager. We do this // by removing the tags that the compiler pass looks for. This means the // url generator can safely be used within tests.