Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit e464954

Browse files
committed
feature #36187 [Routing] Deal with hosts per locale (odolbeau)
This PR was squashed before being merged into the 5.1-dev branch. Discussion ---------- [Routing] Deal with hosts per locale | Q | A | ------------- | --- | Branch? | master | Bug fix? | no | New feature? | yes | Deprecations? | no | Tickets | Fix #30617 | License | MIT | Doc PR | TODO Allow to define a different host for each locale in routing. It's now possible to define this kind of configuration: ```yaml controllers: resource: ../../src/Controller/ type: annotation host: fr: www.example.fr en: www.example.com ``` It's still possible to define an unique host (`host: wwww.example.com`) and if a host is defined for a given route directly, it's not overridden. To be done: - [x] YamlLoader - [x] XmlLoader - [x] PhpLoader? - [x] Documentation - [x] Changelog Commits ------- 4751a73 [Routing] Deal with hosts per locale
2 parents 8f9ff4f + 4751a73 commit e464954

31 files changed

+584
-30
lines changed

src/Symfony/Component/Routing/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ CHANGELOG
1111
* deprecated the `RouteCompiler::REGEX_DELIMITER` constant
1212
* added `ExpressionLanguageProvider` to expose extra functions to route conditions
1313
* added support for a `stateless` keyword for configuring route stateless in PHP, YAML and XML configurations.
14+
* added the "hosts" option to be able to configure the host per locale.
1415

1516
5.0.0
1617
-----

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,13 @@
2020
class CollectionConfigurator
2121
{
2222
use Traits\AddTrait;
23+
use Traits\HostTrait;
2324
use Traits\RouteTrait;
2425

2526
private $parent;
2627
private $parentConfigurator;
2728
private $parentPrefixes;
29+
private $host;
2830

2931
public function __construct(RouteCollection $parent, string $name, self $parentConfigurator = null, array $parentPrefixes = null)
3032
{
@@ -41,6 +43,9 @@ public function __destruct()
4143
if (null === $this->prefixes) {
4244
$this->collection->addPrefix($this->route->getPath());
4345
}
46+
if (null !== $this->host) {
47+
$this->addHost($this->collection, $this->host);
48+
}
4449

4550
$this->parent->addCollection($this->collection);
4651
}
@@ -86,6 +91,20 @@ final public function prefix($prefix): self
8691
return $this;
8792
}
8893

94+
/**
95+
* Sets the host to use for all child routes.
96+
*
97+
* @param string|array $host the host, or the localized hosts
98+
*
99+
* @return $this
100+
*/
101+
final public function host($host): self
102+
{
103+
$this->host = $host;
104+
105+
return $this;
106+
}
107+
89108
private function createRoute(string $path): Route
90109
{
91110
return (clone $this->route)->setPath($path);

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
*/
1919
class ImportConfigurator
2020
{
21+
use Traits\HostTrait;
2122
use Traits\PrefixTrait;
2223
use Traits\RouteTrait;
2324

@@ -59,4 +60,18 @@ final public function namePrefix(string $namePrefix): self
5960

6061
return $this;
6162
}
63+
64+
/**
65+
* Sets the host to use for all child routes.
66+
*
67+
* @param string|array $host the host, or the localized hosts
68+
*
69+
* @return $this
70+
*/
71+
final public function host($host): self
72+
{
73+
$this->addHost($this->route, $host);
74+
75+
return $this;
76+
}
6277
}

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
class RouteConfigurator
2020
{
2121
use Traits\AddTrait;
22+
use Traits\HostTrait;
2223
use Traits\RouteTrait;
2324

2425
protected $parentConfigurator;
@@ -31,4 +32,18 @@ public function __construct(RouteCollection $collection, $route, string $name =
3132
$this->parentConfigurator = $parentConfigurator; // for GC control
3233
$this->prefixes = $prefixes;
3334
}
35+
36+
/**
37+
* Sets the host to use for all child routes.
38+
*
39+
* @param string|array $host the host, or the localized hosts
40+
*
41+
* @return $this
42+
*/
43+
final public function host($host): self
44+
{
45+
$this->addHost($this->route, $host);
46+
47+
return $this;
48+
}
3449
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <[email protected]>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Component\Routing\Loader\Configurator\Traits;
13+
14+
use Symfony\Component\Routing\RouteCollection;
15+
16+
/**
17+
* @internal
18+
*/
19+
trait HostTrait
20+
{
21+
final protected function addHost(RouteCollection $routes, $hosts)
22+
{
23+
if (!$hosts || !\is_array($hosts)) {
24+
$routes->setHost($hosts ?: '');
25+
26+
return;
27+
}
28+
29+
foreach ($routes->all() as $name => $route) {
30+
if (null === $locale = $route->getDefault('_locale')) {
31+
$routes->remove($name);
32+
foreach ($hosts as $locale => $host) {
33+
$localizedRoute = clone $route;
34+
$localizedRoute->setDefault('_locale', $locale);
35+
$localizedRoute->setRequirement('_locale', preg_quote($locale));
36+
$localizedRoute->setDefault('_canonical_route', $name);
37+
$localizedRoute->setHost($host);
38+
$routes->add($name.'.'.$locale, $localizedRoute);
39+
}
40+
} elseif (!isset($hosts[$locale])) {
41+
throw new \InvalidArgumentException(sprintf('Route "%s" with locale "%s" is missing a corresponding host in its parent collection.', $name, $locale));
42+
} else {
43+
$route->setHost($hosts[$locale]);
44+
$route->setRequirement('_locale', preg_quote($locale));
45+
$routes->add($name, $route);
46+
}
47+
}
48+
}
49+
}

src/Symfony/Component/Routing/Loader/Configurator/Traits/LocalizedRouteTrait.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,13 @@ trait LocalizedRouteTrait
2626
* Creates one or many routes.
2727
*
2828
* @param string|array $path the path, or the localized paths of the route
29-
*
30-
* @return Route|RouteCollection
3129
*/
32-
final protected function createLocalizedRoute(RouteCollection $collection, string $name, $path, string $namePrefix = '', array $prefixes = null)
30+
final protected function createLocalizedRoute(RouteCollection $collection, string $name, $path, string $namePrefix = '', array $prefixes = null): RouteCollection
3331
{
3432
$paths = [];
3533

34+
$routes = new RouteCollection();
35+
3636
if (\is_array($path)) {
3737
if (null === $prefixes) {
3838
$paths = $path;
@@ -52,13 +52,12 @@ final protected function createLocalizedRoute(RouteCollection $collection, strin
5252
$paths[$locale] = $prefix.$path;
5353
}
5454
} else {
55-
$collection->add($namePrefix.$name, $route = $this->createRoute($path));
55+
$routes->add($namePrefix.$name, $route = $this->createRoute($path));
56+
$collection->add($namePrefix.$name, $route);
5657

57-
return $route;
58+
return $routes;
5859
}
5960

60-
$routes = new RouteCollection();
61-
6261
foreach ($paths as $locale => $path) {
6362
$routes->add($name.'.'.$locale, $route = $this->createRoute($path));
6463
$collection->add($namePrefix.$name.'.'.$locale, $route);

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

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
use Symfony\Component\Config\Loader\FileLoader;
1515
use Symfony\Component\Config\Resource\FileResource;
1616
use Symfony\Component\Config\Util\XmlUtils;
17+
use Symfony\Component\Routing\Loader\Configurator\Traits\HostTrait;
1718
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
1819
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
1920
use Symfony\Component\Routing\RouteCollection;
@@ -26,6 +27,7 @@
2627
*/
2728
class XmlFileLoader extends FileLoader
2829
{
30+
use HostTrait;
2931
use LocalizedRouteTrait;
3032
use PrefixTrait;
3133

@@ -116,7 +118,7 @@ protected function parseRoute(RouteCollection $collection, \DOMElement $node, st
116118
$schemes = preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY);
117119
$methods = preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY);
118120

119-
list($defaults, $requirements, $options, $condition, $paths) = $this->parseConfigs($node, $filepath);
121+
list($defaults, $requirements, $options, $condition, $paths, /* $prefixes */, $hosts) = $this->parseConfigs($node, $filepath);
120122

121123
$path = $node->getAttribute('path');
122124

@@ -128,14 +130,17 @@ protected function parseRoute(RouteCollection $collection, \DOMElement $node, st
128130
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "path" attribute and <path> child nodes.', $filepath));
129131
}
130132

131-
$route = $this->createLocalizedRoute($collection, $id, $paths ?: $path);
132-
$route->addDefaults($defaults);
133-
$route->addRequirements($requirements);
134-
$route->addOptions($options);
135-
$route->setHost($node->getAttribute('host'));
136-
$route->setSchemes($schemes);
137-
$route->setMethods($methods);
138-
$route->setCondition($condition);
133+
$routes = $this->createLocalizedRoute($collection, $id, $paths ?: $path);
134+
$routes->addDefaults($defaults);
135+
$routes->addRequirements($requirements);
136+
$routes->addOptions($options);
137+
$routes->setSchemes($schemes);
138+
$routes->setMethods($methods);
139+
$routes->setCondition($condition);
140+
141+
if (null !== $hosts) {
142+
$this->addHost($routes, $hosts);
143+
}
139144
}
140145

141146
/**
@@ -155,13 +160,12 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, s
155160

156161
$type = $node->getAttribute('type');
157162
$prefix = $node->getAttribute('prefix');
158-
$host = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
159163
$schemes = $node->hasAttribute('schemes') ? preg_split('/[\s,\|]++/', $node->getAttribute('schemes'), -1, PREG_SPLIT_NO_EMPTY) : null;
160164
$methods = $node->hasAttribute('methods') ? preg_split('/[\s,\|]++/', $node->getAttribute('methods'), -1, PREG_SPLIT_NO_EMPTY) : null;
161165
$trailingSlashOnRoot = $node->hasAttribute('trailing-slash-on-root') ? XmlUtils::phpize($node->getAttribute('trailing-slash-on-root')) : true;
162166
$namePrefix = $node->getAttribute('name-prefix') ?: null;
163167

164-
list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes) = $this->parseConfigs($node, $path);
168+
list($defaults, $requirements, $options, $condition, /* $paths */, $prefixes, $hosts) = $this->parseConfigs($node, $path);
165169

166170
if ('' !== $prefix && $prefixes) {
167171
throw new \InvalidArgumentException(sprintf('The <route> element in file "%s" must not have both a "prefix" attribute and <prefix> child nodes.', $path));
@@ -193,9 +197,10 @@ protected function parseImport(RouteCollection $collection, \DOMElement $node, s
193197
foreach ($imported as $subCollection) {
194198
$this->addPrefix($subCollection, $prefixes ?: $prefix, $trailingSlashOnRoot);
195199

196-
if (null !== $host) {
197-
$subCollection->setHost($host);
200+
if (null !== $hosts) {
201+
$this->addHost($subCollection, $hosts);
198202
}
203+
199204
if (null !== $condition) {
200205
$subCollection->setCondition($condition);
201206
}
@@ -245,6 +250,7 @@ private function parseConfigs(\DOMElement $node, string $path): array
245250
$condition = null;
246251
$prefixes = [];
247252
$paths = [];
253+
$hosts = [];
248254

249255
/** @var \DOMElement $n */
250256
foreach ($node->getElementsByTagNameNS(self::NAMESPACE_URI, '*') as $n) {
@@ -256,6 +262,9 @@ private function parseConfigs(\DOMElement $node, string $path): array
256262
case 'path':
257263
$paths[$n->getAttribute('locale')] = trim($n->textContent);
258264
break;
265+
case 'host':
266+
$hosts[$n->getAttribute('locale')] = trim($n->textContent);
267+
break;
259268
case 'prefix':
260269
$prefixes[$n->getAttribute('locale')] = trim($n->textContent);
261270
break;
@@ -309,7 +318,11 @@ private function parseConfigs(\DOMElement $node, string $path): array
309318
$defaults['_stateless'] = XmlUtils::phpize($stateless);
310319
}
311320

312-
return [$defaults, $requirements, $options, $condition, $paths, $prefixes];
321+
if (!$hosts) {
322+
$hosts = $node->hasAttribute('host') ? $node->getAttribute('host') : null;
323+
}
324+
325+
return [$defaults, $requirements, $options, $condition, $paths, $prefixes, $hosts];
313326
}
314327

315328
/**

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

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
use Symfony\Component\Config\Loader\FileLoader;
1515
use Symfony\Component\Config\Resource\FileResource;
16+
use Symfony\Component\Routing\Loader\Configurator\Traits\HostTrait;
1617
use Symfony\Component\Routing\Loader\Configurator\Traits\LocalizedRouteTrait;
1718
use Symfony\Component\Routing\Loader\Configurator\Traits\PrefixTrait;
1819
use Symfony\Component\Routing\RouteCollection;
@@ -28,6 +29,7 @@
2829
*/
2930
class YamlFileLoader extends FileLoader
3031
{
32+
use HostTrait;
3133
use LocalizedRouteTrait;
3234
use PrefixTrait;
3335

@@ -137,14 +139,17 @@ protected function parseRoute(RouteCollection $collection, string $name, array $
137139
$defaults['_stateless'] = $config['stateless'];
138140
}
139141

140-
$route = $this->createLocalizedRoute($collection, $name, $config['path']);
141-
$route->addDefaults($defaults);
142-
$route->addRequirements($requirements);
143-
$route->addOptions($options);
144-
$route->setHost($config['host'] ?? '');
145-
$route->setSchemes($config['schemes'] ?? []);
146-
$route->setMethods($config['methods'] ?? []);
147-
$route->setCondition($config['condition'] ?? null);
142+
$routes = $this->createLocalizedRoute($collection, $name, $config['path']);
143+
$routes->addDefaults($defaults);
144+
$routes->addRequirements($requirements);
145+
$routes->addOptions($options);
146+
$routes->setSchemes($config['schemes'] ?? []);
147+
$routes->setMethods($config['methods'] ?? []);
148+
$routes->setCondition($config['condition'] ?? null);
149+
150+
if (isset($config['host'])) {
151+
$this->addHost($routes, $config['host']);
152+
}
148153
}
149154

150155
/**
@@ -198,7 +203,7 @@ protected function parseImport(RouteCollection $collection, array $config, strin
198203
$this->addPrefix($subCollection, $prefix, $trailingSlashOnRoot);
199204

200205
if (null !== $host) {
201-
$subCollection->setHost($host);
206+
$this->addHost($subCollection, $host);
202207
}
203208
if (null !== $condition) {
204209
$subCollection->setCondition($condition);

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

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
<xsd:sequence>
4646
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
4747
<xsd:element name="path" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
48+
<xsd:element name="host" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
4849
</xsd:sequence>
4950
<xsd:attribute name="id" type="xsd:string" use="required" />
5051
<xsd:attribute name="path" type="xsd:string" />
@@ -63,6 +64,7 @@
6364
<xsd:group ref="configs" minOccurs="0" maxOccurs="unbounded" />
6465
<xsd:element name="prefix" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
6566
<xsd:element name="exclude" type="xsd:string" minOccurs="0" maxOccurs="unbounded" />
67+
<xsd:element name="host" type="localized-path" minOccurs="0" maxOccurs="unbounded" />
6668
</xsd:sequence>
6769
<xsd:attribute name="resource" type="xsd:string" use="required" />
6870
<xsd:attribute name="type" type="xsd:string" />

0 commit comments

Comments
 (0)