[Routing] allow no-slash root on imported routes
This commit is contained in:
parent
0f9246fcfe
commit
5a98515242
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
use Symfony\Component\Routing\Route;
|
||||
use Symfony\Component\Routing\RouteCollection;
|
||||
|
||||
/**
|
||||
@ -40,16 +41,18 @@ class ImportConfigurator
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function prefix($prefix, string $namePrefix = '')
|
||||
final public function prefix($prefix, bool $trailingSlashOnRoot = true)
|
||||
{
|
||||
if ('' !== $namePrefix) {
|
||||
$this->route->addNamePrefix($namePrefix);
|
||||
}
|
||||
if (!$prefix) {
|
||||
return $this;
|
||||
}
|
||||
if (!\is_array($prefix)) {
|
||||
$this->route->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($this->route->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$prefix[$locale] = trim(trim($localePrefix), '/');
|
||||
@ -61,13 +64,13 @@ class ImportConfigurator
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setPath($localePrefix.$route->getPath());
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$this->route->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($prefix[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
|
||||
} else {
|
||||
$route->setPath($prefix[$locale].$route->getPath());
|
||||
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$this->route->add($name, $route);
|
||||
}
|
||||
}
|
||||
@ -75,4 +78,16 @@ class ImportConfigurator
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the prefix to add to the name of all child routes.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
final public function namePrefix(string $namePrefix)
|
||||
{
|
||||
$this->route->addNamePrefix($namePrefix);
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
@ -158,6 +158,7 @@ class XmlFileLoader extends FileLoader
|
||||
$host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
|
||||
$schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
|
||||
$methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
|
||||
$trailingSlashOnRoot = $node->hasAttribute('trailing-slash-on-root') ? XmlUtils::phpize($node->getAttribute('trailing-slash-on-root')) : true;
|
||||
|
||||
list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes) = $this->parseConfigs($node, $path);
|
||||
|
||||
@ -172,6 +173,14 @@ class XmlFileLoader extends FileLoader
|
||||
|
||||
if ('' !== $prefix || !$prefixes) {
|
||||
$subCollection->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($subCollection->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($prefixes as $locale => $localePrefix) {
|
||||
$prefixes[$locale] = trim(trim($localePrefix), '/');
|
||||
@ -181,7 +190,7 @@ class XmlFileLoader extends FileLoader
|
||||
$subCollection->remove($name);
|
||||
foreach ($prefixes as $locale => $localePrefix) {
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setPath($localePrefix.$route->getPath());
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$subCollection->add($name.'.'.$locale, $localizedRoute);
|
||||
@ -189,7 +198,7 @@ class XmlFileLoader extends FileLoader
|
||||
} elseif (!isset($prefixes[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $path));
|
||||
} else {
|
||||
$route->setPath($prefixes[$locale].$route->getPath());
|
||||
$route->setPath($prefixes[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$subCollection->add($name, $route);
|
||||
}
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ use Symfony\Component\Config\Loader\FileLoader;
|
||||
class YamlFileLoader extends FileLoader
|
||||
{
|
||||
private static $availableKeys = array(
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix',
|
||||
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root',
|
||||
);
|
||||
private $yamlParser;
|
||||
|
||||
@ -155,6 +155,7 @@ class YamlFileLoader extends FileLoader
|
||||
$condition = isset($config['condition']) ? $config['condition'] : null;
|
||||
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
|
||||
$methods = isset($config['methods']) ? $config['methods'] : null;
|
||||
$trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
|
||||
|
||||
if (isset($config['controller'])) {
|
||||
$defaults['_controller'] = $config['controller'];
|
||||
@ -167,6 +168,14 @@ class YamlFileLoader extends FileLoader
|
||||
|
||||
if (!\is_array($prefix)) {
|
||||
$subCollection->addPrefix($prefix);
|
||||
if (!$trailingSlashOnRoot) {
|
||||
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
|
||||
foreach ($subCollection->all() as $route) {
|
||||
if ($route->getPath() === $rootPath) {
|
||||
$route->setPath(rtrim($rootPath, '/'));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
foreach ($prefix as $locale => $localePrefix) {
|
||||
$prefix[$locale] = trim(trim($localePrefix), '/');
|
||||
@ -178,13 +187,13 @@ class YamlFileLoader extends FileLoader
|
||||
$localizedRoute = clone $route;
|
||||
$localizedRoute->setDefault('_locale', $locale);
|
||||
$localizedRoute->setDefault('_canonical_route', $name);
|
||||
$localizedRoute->setPath($localePrefix.$route->getPath());
|
||||
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$subCollection->add($name.'.'.$locale, $localizedRoute);
|
||||
}
|
||||
} elseif (!isset($prefix[$locale])) {
|
||||
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $file));
|
||||
} else {
|
||||
$route->setPath($prefix[$locale].$route->getPath());
|
||||
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
|
||||
$subCollection->add($name, $route);
|
||||
}
|
||||
}
|
||||
|
@ -67,6 +67,7 @@
|
||||
<xsd:attribute name="schemes" type="xsd:string" />
|
||||
<xsd:attribute name="methods" type="xsd:string" />
|
||||
<xsd:attribute name="controller" type="xsd:string" />
|
||||
<xsd:attribute name="trailing-slash-on-root" type="xsd:boolean" />
|
||||
</xsd:complexType>
|
||||
|
||||
<xsd:complexType name="default" mixed="true">
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<routes xmlns="http://symfony.com/schema/routing"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://symfony.com/schema/routing
|
||||
http://symfony.com/schema/routing/routing-1.0.xsd">
|
||||
|
||||
<import resource="../controller/routing.xml" prefix="/slash" name-prefix="a_" />
|
||||
<import resource="../controller/routing.xml" prefix="/no-slash" name-prefix="b_" trailing-slash-on-root="false" />
|
||||
|
||||
</routes>
|
@ -0,0 +1,10 @@
|
||||
app:
|
||||
resource: ../controller/routing.yml
|
||||
name_prefix: a_
|
||||
prefix: /slash
|
||||
|
||||
api:
|
||||
resource: ../controller/routing.yml
|
||||
name_prefix: b_
|
||||
prefix: /no-slash
|
||||
trailing_slash_on_root: false
|
@ -16,7 +16,11 @@ return function (RoutingConfigurator $routes) {
|
||||
->requirements(array('id' => '\d+'));
|
||||
|
||||
$routes->import('php_dsl_sub.php')
|
||||
->prefix('/zub', 'z_');
|
||||
->namePrefix('z_')
|
||||
->prefix('/zub');
|
||||
|
||||
$routes->import('php_dsl_sub_root.php')
|
||||
->prefix('/bus', false);
|
||||
|
||||
$routes->add('ouf', '/ouf')
|
||||
->schemes(array('https'))
|
||||
|
@ -6,6 +6,7 @@ return function (RoutingConfigurator $routes) {
|
||||
$add = $routes->collection('c_')
|
||||
->prefix('pub');
|
||||
|
||||
$add('root', '/');
|
||||
$add('bar', '/bar');
|
||||
|
||||
$add->collection('pub_')
|
||||
|
@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Component\Routing\Loader\Configurator;
|
||||
|
||||
return function (RoutingConfigurator $routes) {
|
||||
$add = $routes->collection('r_');
|
||||
|
||||
$add('root', '/');
|
||||
$add('bar', '/bar/');
|
||||
};
|
@ -99,6 +99,9 @@ class PhpFileLoaderTest extends TestCase
|
||||
$expectedCollection->add('buz', (new Route('/zub'))
|
||||
->setDefaults(array('_controller' => 'foo:act'))
|
||||
);
|
||||
$expectedCollection->add('c_root', (new Route('/sub/pub/'))
|
||||
->setRequirements(array('id' => '\d+'))
|
||||
);
|
||||
$expectedCollection->add('c_bar', (new Route('/sub/pub/bar'))
|
||||
->setRequirements(array('id' => '\d+'))
|
||||
);
|
||||
@ -106,8 +109,11 @@ class PhpFileLoaderTest extends TestCase
|
||||
->setHost('host')
|
||||
->setRequirements(array('id' => '\d+'))
|
||||
);
|
||||
$expectedCollection->add('z_c_root', new Route('/zub/pub/'));
|
||||
$expectedCollection->add('z_c_bar', new Route('/zub/pub/bar'));
|
||||
$expectedCollection->add('z_c_pub_buz', (new Route('/zub/pub/buz'))->setHost('host'));
|
||||
$expectedCollection->add('r_root', new Route('/bus'));
|
||||
$expectedCollection->add('r_bar', new Route('/bus/bar/'));
|
||||
$expectedCollection->add('ouf', (new Route('/ouf'))
|
||||
->setSchemes(array('https'))
|
||||
->setMethods(array('GET'))
|
||||
@ -115,6 +121,7 @@ class PhpFileLoaderTest extends TestCase
|
||||
);
|
||||
|
||||
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub.php')));
|
||||
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub_root.php')));
|
||||
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl.php')));
|
||||
|
||||
$this->assertEquals($expectedCollection, $routeCollection);
|
||||
|
@ -411,4 +411,13 @@ class XmlFileLoaderTest extends TestCase
|
||||
$this->assertNotNull($routeCollection->get('api_app_blog'));
|
||||
$this->assertEquals('/api/blog', $routeCollection->get('api_app_blog')->getPath());
|
||||
}
|
||||
|
||||
public function testImportRouteWithNoTrailingSlash()
|
||||
{
|
||||
$loader = new XmlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_no_trailing_slash')));
|
||||
$routeCollection = $loader->load('routing.xml');
|
||||
|
||||
$this->assertEquals('/slash/', $routeCollection->get('a_app_homepage')->getPath());
|
||||
$this->assertEquals('/no-slash', $routeCollection->get('b_app_homepage')->getPath());
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +209,6 @@ class YamlFileLoaderTest extends TestCase
|
||||
$this->assertCount(3, $routes);
|
||||
}
|
||||
|
||||
|
||||
public function testImportingRoutesFromDefinition()
|
||||
{
|
||||
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/localized')));
|
||||
@ -275,4 +274,13 @@ class YamlFileLoaderTest extends TestCase
|
||||
$this->assertEquals('DefaultController::defaultAction', $routes->get('home.nl')->getDefault('_controller'));
|
||||
$this->assertEquals('DefaultController::defaultAction', $routes->get('not_localized')->getDefault('_controller'));
|
||||
}
|
||||
|
||||
public function testImportRouteWithNoTrailingSlash()
|
||||
{
|
||||
$loader = new YamlFileLoader(new FileLocator(array(__DIR__.'/../Fixtures/import_with_no_trailing_slash')));
|
||||
$routeCollection = $loader->load('routing.yml');
|
||||
|
||||
$this->assertEquals('/slash/', $routeCollection->get('a_app_homepage')->getPath());
|
||||
$this->assertEquals('/no-slash', $routeCollection->get('b_app_homepage')->getPath());
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user