diff --git a/core/lib/Drupal/Core/Path/MemoryAliasManager.php b/core/lib/Drupal/Core/Path/MemoryAliasManager.php new file mode 100644 index 0000000..71fc734 --- /dev/null +++ b/core/lib/Drupal/Core/Path/MemoryAliasManager.php @@ -0,0 +1,92 @@ +langcode = language(LANGUAGE_TYPE_URL)->langcode; + } + + /** + * Implements \Drupal\Core\Path\AliasManagerInterface::getSystemPath(). + */ + public function getSystemPath($path, $path_language = NULL) { + $result = $path; + $langcode = $path_language ?: $this->langcode; + + // A specific langcode overrides in the not specified case. + if (isset($this->pathAliasMap[$langcode][$path])) { + $result = $this->pathAliasMap[$langcode][$path]; + } + elseif (isset($this->pathAliasMap[LANGUAGE_NOT_SPECIFIED][$path])) { + $result = $this->pathAliasMap[LANGUAGE_NOT_SPECIFIED][$path]; + } + return $result; + } + + /** + * Implements \Drupal\Core\Path\AliasManagerInterface::getPathAlias(). + */ + public function getPathAlias($path, $path_language = NULL) { + $result = $path; + $langcode = $path_language ?: $this->langcode; + + // @todo Should we store both possible maps to not use arraySearch. + // A specific langcode overrides in the not specified case. + // array_reverse is used, because newer path aliases should be preferred. + if (isset($this->pathAliasMap[$langcode]) && $alias = array_search($path, array_reverse($this->pathAliasMap[$langcode]))) { + $result = $alias; + } + elseif (isset($this->pathAliasMap[LANGUAGE_NOT_SPECIFIED]) && $alias = array_search($path, array_reverse($this->pathAliasMap[LANGUAGE_NOT_SPECIFIED]))) { + $result = $alias; + } + return $result; + + } + + /** + * Implements \Drupal\Core\Path\AliasManagerInterface::getPathLookups(). + */ + public function getPathLookups() { + return $this->pathAliasMap; + } + + /** + * Implements \Drupal\Core\Path\AliasManagerInterface::preloadPathLookups(). + */ + public function preloadPathLookups(array $path_list) { + $this->pathAliasMap = $path_list; + } + +} diff --git a/core/lib/Drupal/Core/Path/Path.php b/core/lib/Drupal/Core/Path/Path.php index a7fd55a..4db28a9 100644 --- a/core/lib/Drupal/Core/Path/Path.php +++ b/core/lib/Drupal/Core/Path/Path.php @@ -28,14 +28,14 @@ class Path { * @param \Drupal\Core\Database\Connection $connection * A database connection for reading and writing path aliases. * - * @param \Drupal\Core\Path\AliasManager $alias_manager + * @param \Drupal\Core\Path\AliasManagerInterface $alias_manager * An alias manager with an internal cache of stored aliases. * * @todo This class should not take an alias manager in its constructor. Once * we move to firing an event for CRUD operations instead of invoking a * hook, we can have a listener that calls cacheClear() on the alias manager. */ - public function __construct(Connection $connection, AliasManager $alias_manager) { + public function __construct(Connection $connection, AliasManagerInterface $alias_manager) { $this->connection = $connection; $this->alias_manager = $alias_manager; } diff --git a/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php b/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php index a8cec27..ddfba6c 100644 --- a/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php +++ b/core/modules/edit/lib/Drupal/edit/Tests/MetadataGeneratorTest.php @@ -125,7 +125,6 @@ function testSimpleEntityType() { } function testEditorWithCustomMetadata() { - $this->installSchema('system', 'url_alias'); $this->enableModules(array('user', 'filter')); // Enable edit_test module so that the WYSIWYG Create.js PropertyEditor diff --git a/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php b/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php index f85f176..7a7c79e 100644 --- a/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php +++ b/core/modules/editor/lib/Drupal/editor/Tests/EditorManagerTest.php @@ -42,7 +42,6 @@ function setUp() { parent::setUp(); // Install the Filter module. - $this->installSchema('system', 'url_alias'); $this->enableModules(array('user', 'filter')); // Add text formats. diff --git a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php index fd37fb8..57a2cdb 100644 --- a/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php +++ b/core/modules/filter/lib/Drupal/filter/Tests/FilterDefaultConfigTest.php @@ -27,8 +27,6 @@ public static function getInfo() { function setUp() { parent::setUp(); $this->enableModules(array('user')); - // filter_permission() calls into url() to output a link in the description. - $this->installSchema('system', 'url_alias'); } /** diff --git a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php index b09df75..d1f88cf 100644 --- a/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php +++ b/core/modules/simpletest/lib/Drupal/simpletest/DrupalUnitTestBase.php @@ -154,6 +154,14 @@ public function containerBuild($container) { ->register('keyvalue', 'Drupal\Core\KeyValueStore\KeyValueFactory') ->addArgument(new Reference('service_container')); } + + + // Register a memory only path alias manager. + $container->register('path.alias_manager', 'Drupal\Core\Path\MemoryAliasManager'); + + $container->register('path.alias_manager.cached', 'Drupal\Core\CacheDecorator\AliasManagerCacheDecorator') + ->addArgument(new Reference('path.alias_manager')) + ->addArgument(new Reference('cache.path')); } /** diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/AliasManagerTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Path/AliasManagerTestBase.php new file mode 100644 index 0000000..2a3c9ee --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Path/AliasManagerTestBase.php @@ -0,0 +1,101 @@ + "user/1", + 'alias' => 'foo', + ); + + $this->path->save($path['source'], $path['alias']); + $this->assertEqual($this->aliasManager->getPathAlias($path['source']), $path['alias'], 'Basic alias lookup works.'); + $this->assertEqual($this->aliasManager->getSystemPath($path['alias']), $path['source'], 'Basic source lookup works.'); + + // Create a language specific alias for the default language (English). + $path = array( + 'source' => "user/1", + 'alias' => "users/Dries", + 'langcode' => 'en', + ); + $this->path->save($path['source'], $path['alias'], $path['langcode']); + $this->assertEqual($this->aliasManager->getPathAlias($path['source']), $path['alias'], 'English alias overrides language-neutral alias.'); + $this->assertEqual($this->aliasManager->getSystemPath($path['alias']), $path['source'], 'English source overrides language-neutral source.'); + + // Create a language-neutral alias for the same path, again. + $path = array( + 'source' => "user/1", + 'alias' => 'bar', + ); + $this->path->save($path['source'], $path['alias']); + $this->assertEqual($this->aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a language-neutral alias.'); + + // Create a language-specific (xx-lolspeak) alias for the same path. + $path = array( + 'source' => "user/1", + 'alias' => 'LOL', + 'langcode' => 'xx-lolspeak', + ); + $this->path->save($path['source'], $path['alias'], $path['langcode']); + $this->assertEqual($this->aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a LOLspeak alias.'); + // The LOLspeak alias should be returned if we really want LOLspeak. + $this->assertEqual($this->aliasManager->getPathAlias($path['source'], 'xx-lolspeak'), 'LOL', '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 = array( + 'source' => "user/1", + 'alias' => 'users/my-new-path', + 'langcode' => 'en', + ); + $this->path->save($path['source'], $path['alias'], $path['langcode']); + $this->assertEqual($this->aliasManager->getPathAlias($path['source']), $path['alias'], 'Recently created English alias returned.'); + $this->assertEqual($this->aliasManager->getSystemPath($path['alias']), $path['source'], 'Recently created English source returned.'); + + // Remove the English aliases, which should cause a fallback to the most + // recently created language-neutral alias, 'bar'. + $this->path->delete(array('langcode' => 'en')); + $this->assertEqual($this->aliasManager->getPathAlias($path['source']), 'bar', '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. + $this->path->save('user/2', 'bar'); + $this->assertEqual($this->aliasManager->getSystemPath('bar'), 'user/2', 'Newer alias record is returned when comparing two LANGUAGE_NOT_SPECIFIED paths with the same alias.'); + } +} diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php index 21a129a..b42937e 100644 --- a/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php +++ b/core/modules/system/lib/Drupal/system/Tests/Path/AliasTest.php @@ -2,20 +2,33 @@ /** * @file - * Definition of Drupal\system\Tests\Path\CrudTest. + * Contains \Drupal\system\Tests\Path\CrudTest. */ namespace Drupal\system\Tests\Path; -use Drupal\simpletest\DrupalUnitTestBase; use Drupal\Core\Database\Database; use Drupal\Core\Path\Path; use Drupal\Core\Path\AliasManager; /** - * Tests path alias CRUD and lookup functionality. + * Tests path alias CRUD and lookup for the database alias manager. */ -class AliasTest extends DrupalUnitTestBase { +class AliasTest extends AliasManagerTestBase { + + /** + * Stores a class to generate some example test aliases. + * + * @var \Drupal\system\Tests\Path\UrlAliasFixtures + */ + protected $fixtures; + + /** + * Stores the database connection used by this test. + * + * @var \Drupal\Core\Database\Connection + */ + protected $connection; public static function getInfo() { return array( @@ -27,7 +40,18 @@ public static function getInfo() { public function setUp() { parent::setUp(); + + // Prepare database. + $this->connection = $this->container->get('database'); + $this->fixtures = new UrlAliasFixtures(); + $this->fixtures->createTables($this->connection); + + // In contrast to all other Drupal unit test cases this one uses the alias + // manager which uses the database. + $keyvalue = $this->container->get('keyvalue'); + $this->aliasManager = new AliasManager($this->connection, $keyvalue); + $this->path = new Path($this->connection, $this->aliasManager); } public function tearDown() { @@ -36,23 +60,15 @@ public function tearDown() { parent::tearDown(); } - function testCRUD() { - //Prepare database table. - $connection = Database::getConnection(); - $this->fixtures->createTables($connection); - - //Create AliasManager and Path object. - $aliasManager = new AliasManager($connection, $this->container->get('keyvalue')); - $path = new Path($connection, $aliasManager); - + // Prepare aliases. $aliases = $this->fixtures->sampleUrlAliases(); //Create a few aliases foreach ($aliases as $idx => $alias) { - $path->save($alias['source'], $alias['alias'], $alias['langcode']); + $this->path->save($alias['source'], $alias['alias'], $alias['langcode']); - $result = $connection->query('SELECT * FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'], ':langcode' => $alias['langcode'])); + $result = $this->connection->query('SELECT * FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'], ':langcode' => $alias['langcode'])); $rows = $result->fetchAll(); $this->assertEqual(count($rows), 1, format_string('Created an entry for %alias.', array('%alias' => $alias['alias']))); @@ -64,15 +80,15 @@ function testCRUD() { //Load a few aliases foreach ($aliases as $alias) { $pid = $alias['pid']; - $loadedAlias = $path->load(array('pid' => $pid)); + $loadedAlias = $this->path->load(array('pid' => $pid)); $this->assertEqual($loadedAlias, $alias, format_string('Loaded the expected path with pid %pid.', array('%pid' => $pid))); } //Update a few aliases foreach ($aliases as $alias) { - $path->save($alias['source'], $alias['alias'] . '_updated', $alias['langcode'], $alias['pid']); + $this->path->save($alias['source'], $alias['alias'] . '_updated', $alias['langcode'], $alias['pid']); - $result = $connection->query('SELECT pid FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'] . '_updated', ':langcode' => $alias['langcode'])); + $result = $this->connection->query('SELECT pid FROM {url_alias} WHERE source = :source AND alias= :alias AND langcode = :langcode', array(':source' => $alias['source'], ':alias' => $alias['alias'] . '_updated', ':langcode' => $alias['langcode'])); $pid = $result->fetchField(); $this->assertEqual($pid, $alias['pid'], format_string('Updated entry for pid %pid.', array('%pid' => $pid))); @@ -81,83 +97,13 @@ function testCRUD() { //Delete a few aliases foreach ($aliases as $alias) { $pid = $alias['pid']; - $path->delete(array('pid' => $pid)); + $this->path->delete(array('pid' => $pid)); - $result = $connection->query('SELECT * FROM {url_alias} WHERE pid = :pid', array(':pid' => $pid)); + $result = $this->connection->query('SELECT * FROM {url_alias} WHERE pid = :pid', array(':pid' => $pid)); $rows = $result->fetchAll(); $this->assertEqual(count($rows), 0, format_string('Deleted entry with pid %pid.', array('%pid' => $pid))); } } - function testLookupPath() { - //Prepare database table. - $connection = Database::getConnection(); - $this->fixtures->createTables($connection); - - //Create AliasManager and Path object. - $aliasManager = new AliasManager($connection, $this->container->get('keyvalue')); - $pathObject = new Path($connection, $aliasManager); - - // Test the situation where the source is the same for multiple aliases. - // Start with a language-neutral alias, which we will override. - $path = array( - 'source' => "user/1", - 'alias' => 'foo', - ); - - $pathObject->save($path['source'], $path['alias']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'Basic alias lookup works.'); - $this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'Basic source lookup works.'); - - // Create a language specific alias for the default language (English). - $path = array( - 'source' => "user/1", - 'alias' => "users/Dries", - 'langcode' => 'en', - ); - $pathObject->save($path['source'], $path['alias'], $path['langcode']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'English alias overrides language-neutral alias.'); - $this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'English source overrides language-neutral source.'); - - // Create a language-neutral alias for the same path, again. - $path = array( - 'source' => "user/1", - 'alias' => 'bar', - ); - $pathObject->save($path['source'], $path['alias']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a language-neutral alias.'); - - // Create a language-specific (xx-lolspeak) alias for the same path. - $path = array( - 'source' => "user/1", - 'alias' => 'LOL', - 'langcode' => 'xx-lolspeak', - ); - $pathObject->save($path['source'], $path['alias'], $path['langcode']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), "users/Dries", 'English alias still returned after entering a LOLspeak alias.'); - // The LOLspeak alias should be returned if we really want LOLspeak. - $this->assertEqual($aliasManager->getPathAlias($path['source'], 'xx-lolspeak'), 'LOL', '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 = array( - 'source' => "user/1", - 'alias' => 'users/my-new-path', - 'langcode' => 'en', - ); - $pathObject->save($path['source'], $path['alias'], $path['langcode']); - $this->assertEqual($aliasManager->getPathAlias($path['source']), $path['alias'], 'Recently created English alias returned.'); - $this->assertEqual($aliasManager->getSystemPath($path['alias']), $path['source'], 'Recently created English source returned.'); - - // Remove the English aliases, which should cause a fallback to the most - // recently created language-neutral alias, 'bar'. - $pathObject->delete(array('langcode' => 'en')); - $this->assertEqual($aliasManager->getPathAlias($path['source']), 'bar', '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. - $pathObject->save('user/2', 'bar'); - $this->assertEqual($aliasManager->getSystemPath('bar'), 'user/2', 'Newer alias record is returned when comparing two LANGUAGE_NOT_SPECIFIED paths with the same alias.'); - } } diff --git a/core/modules/system/lib/Drupal/system/Tests/Path/MemoryAliasTest.php b/core/modules/system/lib/Drupal/system/Tests/Path/MemoryAliasTest.php new file mode 100644 index 0000000..451cea4 --- /dev/null +++ b/core/modules/system/lib/Drupal/system/Tests/Path/MemoryAliasTest.php @@ -0,0 +1,35 @@ + t('Memory Path Alias Unit Tests'), + 'description' => t('Tests the memory path alias manager.'), + 'group' => t('Path API'), + ); + } + + public function setUp() { + parent::setUp(); + + // Let the test use the memory alias manager. + $this->path = $this->container->get('path.crud'); + $this->aliasManager = $this->container->get('path.alias_manager'); + } + +}