Skip to content

Commit 7262c59

Browse files
committed
feature #26284 [Routing] allow no-slash root on imported routes (nicolas-grekas)
This PR was merged into the 4.1-dev branch. Discussion ---------- [Routing] allow no-slash root on imported routes | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #12141 | License | MIT | Doc PR | - With this change, a collection is imported, its root can have no slash appended. e.g.: ```yaml import: resource: ... trailing_slash_on_root: false ``` Works also for XML and PHP-DSL. Commits ------- 5a98515 [Routing] allow no-slash root on imported routes
2 parents 07a2f6c + 5a98515 commit 7262c59

File tree

12 files changed

+109
-16
lines changed

12 files changed

+109
-16
lines changed

src/Symfony/Component/Routing/Loader/Configurator/ImportConfigurator.php

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
namespace Symfony\Component\Routing\Loader\Configurator;
1313

14+
use Symfony\Component\Routing\Route;
1415
use Symfony\Component\Routing\RouteCollection;
1516

1617
/**
@@ -40,16 +41,18 @@ public function __destruct()
4041
*
4142
* @return $this
4243
*/
43-
final public function prefix($prefix, string $namePrefix = '')
44+
final public function prefix($prefix, bool $trailingSlashOnRoot = true)
4445
{
45-
if ('' !== $namePrefix) {
46-
$this->route->addNamePrefix($namePrefix);
47-
}
48-
if (!$prefix) {
49-
return $this;
50-
}
5146
if (!\is_array($prefix)) {
5247
$this->route->addPrefix($prefix);
48+
if (!$trailingSlashOnRoot) {
49+
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
50+
foreach ($this->route->all() as $route) {
51+
if ($route->getPath() === $rootPath) {
52+
$route->setPath(rtrim($rootPath, '/'));
53+
}
54+
}
55+
}
5356
} else {
5457
foreach ($prefix as $locale => $localePrefix) {
5558
$prefix[$locale] = trim(trim($localePrefix), '/');
@@ -61,18 +64,30 @@ final public function prefix($prefix, string $namePrefix = '')
6164
$localizedRoute = clone $route;
6265
$localizedRoute->setDefault('_locale', $locale);
6366
$localizedRoute->setDefault('_canonical_route', $name);
64-
$localizedRoute->setPath($localePrefix.$route->getPath());
67+
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
6568
$this->route->add($name.'.'.$locale, $localizedRoute);
6669
}
6770
} elseif (!isset($prefix[$locale])) {
6871
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix in its parent collection.', $name, $locale));
6972
} else {
70-
$route->setPath($prefix[$locale].$route->getPath());
73+
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
7174
$this->route->add($name, $route);
7275
}
7376
}
7477
}
7578

7679
return $this;
7780
}
81+
82+
/**
83+
* Sets the prefix to add to the name of all child routes.
84+
*
85+
* @return $this
86+
*/
87+
final public function namePrefix(string $namePrefix)
88+
{
89+
$this->route->addNamePrefix($namePrefix);
90+
91+
return $this;
92+
}
7893
}

src/Symfony/Component/Routing/Loader/XmlFileLoader.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, $
158158
$host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
159159
$schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
160160
$methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
161+
$trailingSlashOnRoot = $node->hasAttribute('trailing-slash-on-root') ? XmlUtils::phpize($node->getAttribute('trailing-slash-on-root')) : true;
161162

162163
list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes) = $this->parseConfigs($node, $path);
163164

@@ -172,6 +173,14 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, $
172173

173174
if ('' !== $prefix || !$prefixes) {
174175
$subCollection->addPrefix($prefix);
176+
if (!$trailingSlashOnRoot) {
177+
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
178+
foreach ($subCollection->all() as $route) {
179+
if ($route->getPath() === $rootPath) {
180+
$route->setPath(rtrim($rootPath, '/'));
181+
}
182+
}
183+
}
175184
} else {
176185
foreach ($prefixes as $locale => $localePrefix) {
177186
$prefixes[$locale] = trim(trim($localePrefix), '/');
@@ -181,15 +190,15 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, $
181190
$subCollection->remove($name);
182191
foreach ($prefixes as $locale => $localePrefix) {
183192
$localizedRoute = clone $route;
184-
$localizedRoute->setPath($localePrefix.$route->getPath());
193+
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
185194
$localizedRoute->setDefault('_locale', $locale);
186195
$localizedRoute->setDefault('_canonical_route', $name);
187196
$subCollection->add($name.'.'.$locale, $localizedRoute);
188197
}
189198
} elseif (!isset($prefixes[$locale])) {
190199
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $path));
191200
} else {
192-
$route->setPath($prefixes[$locale].$route->getPath());
201+
$route->setPath($prefixes[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
193202
$subCollection->add($name, $route);
194203
}
195204
}

src/Symfony/Component/Routing/Loader/YamlFileLoader.php

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
class YamlFileLoader extends FileLoader
2929
{
3030
private static $availableKeys = array(
31-
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix',
31+
'resource', 'type', 'prefix', 'path', 'host', 'schemes', 'methods', 'defaults', 'requirements', 'options', 'condition', 'controller', 'name_prefix', 'trailing_slash_on_root',
3232
);
3333
private $yamlParser;
3434

@@ -155,6 +155,7 @@ protected function parseImport(RouteCollection $collection, array $config, $path
155155
$condition = isset($config['condition']) ? $config['condition'] : null;
156156
$schemes = isset($config['schemes']) ? $config['schemes'] : null;
157157
$methods = isset($config['methods']) ? $config['methods'] : null;
158+
$trailingSlashOnRoot = $config['trailing_slash_on_root'] ?? true;
158159

159160
if (isset($config['controller'])) {
160161
$defaults['_controller'] = $config['controller'];
@@ -167,6 +168,14 @@ protected function parseImport(RouteCollection $collection, array $config, $path
167168

168169
if (!\is_array($prefix)) {
169170
$subCollection->addPrefix($prefix);
171+
if (!$trailingSlashOnRoot) {
172+
$rootPath = (new Route(trim(trim($prefix), '/').'/'))->getPath();
173+
foreach ($subCollection->all() as $route) {
174+
if ($route->getPath() === $rootPath) {
175+
$route->setPath(rtrim($rootPath, '/'));
176+
}
177+
}
178+
}
170179
} else {
171180
foreach ($prefix as $locale => $localePrefix) {
172181
$prefix[$locale] = trim(trim($localePrefix), '/');
@@ -178,13 +187,13 @@ protected function parseImport(RouteCollection $collection, array $config, $path
178187
$localizedRoute = clone $route;
179188
$localizedRoute->setDefault('_locale', $locale);
180189
$localizedRoute->setDefault('_canonical_route', $name);
181-
$localizedRoute->setPath($localePrefix.$route->getPath());
190+
$localizedRoute->setPath($localePrefix.(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
182191
$subCollection->add($name.'.'.$locale, $localizedRoute);
183192
}
184193
} elseif (!isset($prefix[$locale])) {
185194
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding prefix when imported in "%s".', $name, $locale, $file));
186195
} else {
187-
$route->setPath($prefix[$locale].$route->getPath());
196+
$route->setPath($prefix[$locale].(!$trailingSlashOnRoot && '/' === $route->getPath() ? '' : $route->getPath()));
188197
$subCollection->add($name, $route);
189198
}
190199
}

src/Symfony/Component/Routing/Loader/schema/routing/routing-1.0.xsd

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,7 @@
6767
<xsd:attribute name="schemes" type="xsd:string" />
6868
<xsd:attribute name="methods" type="xsd:string" />
6969
<xsd:attribute name="controller" type="xsd:string" />
70+
<xsd:attribute name="trailing-slash-on-root" type="xsd:boolean" />
7071
</xsd:complexType>
7172

7273
<xsd:complexType name="default" mixed="true">
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?xml version="1.0" encoding="UTF-8" ?>
2+
<routes xmlns="http://symfony.com/schema/routing"
3+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4+
xsi:schemaLocation="http://symfony.com/schema/routing
5+
http://symfony.com/schema/routing/routing-1.0.xsd">
6+
7+
<import resource="../controller/routing.xml" prefix="/slash" name-prefix="a_" />
8+
<import resource="../controller/routing.xml" prefix="/no-slash" name-prefix="b_" trailing-slash-on-root="false" />
9+
10+
</routes>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
app:
2+
resource: ../controller/routing.yml
3+
name_prefix: a_
4+
prefix: /slash
5+
6+
api:
7+
resource: ../controller/routing.yml
8+
name_prefix: b_
9+
prefix: /no-slash
10+
trailing_slash_on_root: false

src/Symfony/Component/Routing/Tests/Fixtures/php_dsl.php

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,11 @@
1616
->requirements(array('id' => '\d+'));
1717

1818
$routes->import('php_dsl_sub.php')
19-
->prefix('/zub', 'z_');
19+
->namePrefix('z_')
20+
->prefix('/zub');
21+
22+
$routes->import('php_dsl_sub_root.php')
23+
->prefix('/bus', false);
2024

2125
$routes->add('ouf', '/ouf')
2226
->schemes(array('https'))

src/Symfony/Component/Routing/Tests/Fixtures/php_dsl_sub.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
$add = $routes->collection('c_')
77
->prefix('pub');
88

9+
$add('root', '/');
910
$add('bar', '/bar');
1011

1112
$add->collection('pub_')
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
<?php
2+
3+
namespace Symfony\Component\Routing\Loader\Configurator;
4+
5+
return function (RoutingConfigurator $routes) {
6+
$add = $routes->collection('r_');
7+
8+
$add('root', '/');
9+
$add('bar', '/bar/');
10+
};

src/Symfony/Component/Routing/Tests/Loader/PhpFileLoaderTest.php

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,22 +99,29 @@ public function testRoutingConfigurator()
9999
$expectedCollection->add('buz', (new Route('/zub'))
100100
->setDefaults(array('_controller' => 'foo:act'))
101101
);
102+
$expectedCollection->add('c_root', (new Route('/sub/pub/'))
103+
->setRequirements(array('id' => '\d+'))
104+
);
102105
$expectedCollection->add('c_bar', (new Route('/sub/pub/bar'))
103106
->setRequirements(array('id' => '\d+'))
104107
);
105108
$expectedCollection->add('c_pub_buz', (new Route('/sub/pub/buz'))
106109
->setHost('host')
107110
->setRequirements(array('id' => '\d+'))
108111
);
112+
$expectedCollection->add('z_c_root', new Route('/zub/pub/'));
109113
$expectedCollection->add('z_c_bar', new Route('/zub/pub/bar'));
110114
$expectedCollection->add('z_c_pub_buz', (new Route('/zub/pub/buz'))->setHost('host'));
115+
$expectedCollection->add('r_root', new Route('/bus'));
116+
$expectedCollection->add('r_bar', new Route('/bus/bar/'));
111117
$expectedCollection->add('ouf', (new Route('/ouf'))
112118
->setSchemes(array('https'))
113119
->setMethods(array('GET'))
114120
->setDefaults(array('id' => 0))
115121
);
116122

117123
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub.php')));
124+
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl_sub_root.php')));
118125
$expectedCollection->addResource(new FileResource(realpath(__DIR__.'/../Fixtures/php_dsl.php')));
119126

120127
$this->assertEquals($expectedCollection, $routeCollection);

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy