Skip to content

Commit 34568da

Browse files
committed
Allow query-specific parameters in URL generator using _query
1 parent 5743ca3 commit 34568da

File tree

3 files changed

+91
-0
lines changed

3 files changed

+91
-0
lines changed

src/Symfony/Component/Routing/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,11 @@
11
CHANGELOG
22
=========
33

4+
7.4
5+
---
6+
7+
* Allow query-specific parameters in `UrlGenerator` using `_query`
8+
49
7.3
510
---
611

src/Symfony/Component/Routing/Generator/UrlGenerator.php

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,17 @@ public function generate(string $name, array $parameters = [], int $referenceTyp
142142
*/
143143
protected function doGenerate(array $variables, array $defaults, array $requirements, array $tokens, array $parameters, string $name, int $referenceType, array $hostTokens, array $requiredSchemes = []): string
144144
{
145+
$queryParameters = [];
146+
147+
if (isset($parameters['_query'])) {
148+
if (is_array($parameters['_query'])) {
149+
$queryParameters = $parameters['_query'];
150+
unset($parameters['_query']);
151+
} else {
152+
trigger_deprecation('symfony/routing', '7.4', 'Parameter "_query" is reserved for passing an array of query parameters. Passing a scalar value is deprecated and will throw an exception in Symfony 8.0.');
153+
}
154+
}
155+
145156
$variables = array_flip($variables);
146157
$mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);
147158

@@ -260,6 +271,7 @@ protected function doGenerate(array $variables, array $defaults, array $requirem
260271

261272
// add a query string if needed
262273
$extra = array_udiff_assoc(array_diff_key($parameters, $variables), $defaults, fn ($a, $b) => $a == $b ? 0 : 1);
274+
$extra = array_merge($extra, $queryParameters);
263275

264276
array_walk_recursive($extra, $caster = static function (&$v) use (&$caster) {
265277
if (\is_object($v)) {

src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1054,6 +1054,80 @@ public function testUtf8VarName()
10541054
$this->assertSame('/app.php/foo/baz', $this->getGenerator($routes)->generate('test', ['bär' => 'baz']));
10551055
}
10561056

1057+
public function testQueryParameters()
1058+
{
1059+
$routes = $this->getRoutes('user', new Route('/user/{username}'));
1060+
$url = $this->getGenerator($routes)->generate('user', [
1061+
'username' => 'john',
1062+
'a' => 'foo',
1063+
'b' => 'bar',
1064+
'c' => 'baz',
1065+
'_query' => [
1066+
'a' => '123',
1067+
'd' => '789',
1068+
],
1069+
]);
1070+
$this->assertSame('/app.php/user/john?a=123&b=bar&c=baz&d=789', $url);
1071+
}
1072+
1073+
public function testRouteHostParameterAndQueryParameterWithSameName()
1074+
{
1075+
$routes = $this->getRoutes('admin_stats', new Route('/admin/stats', requirements: ['domain' => '.+'], host: '{siteCode}.{domain}'));
1076+
$url = $this->getGenerator($routes)->generate('admin_stats', [
1077+
'siteCode' => 'fr',
1078+
'domain' => 'example.com',
1079+
'_query' => [
1080+
'siteCode' => 'us',
1081+
],
1082+
], UrlGeneratorInterface::NETWORK_PATH);
1083+
$this->assertSame('//fr.example.com/app.php/admin/stats?siteCode=us', $url);
1084+
}
1085+
1086+
public function testRoutePathParameterAndQueryParameterWithSameName()
1087+
{
1088+
$routes = $this->getRoutes('user', new Route('/user/{id}'));
1089+
$url = $this->getGenerator($routes)->generate('user', [
1090+
'id' => '123',
1091+
'_query' => [
1092+
'id' => '456',
1093+
],
1094+
]);
1095+
$this->assertSame('/app.php/user/123?id=456', $url);
1096+
}
1097+
1098+
public function testQueryParameterCannotSubstituteRouteParameter()
1099+
{
1100+
$routes = $this->getRoutes('user', new Route('/user/{id}'));
1101+
1102+
$this->expectException(MissingMandatoryParametersException::class);
1103+
$this->expectExceptionMessage('Some mandatory parameters are missing ("id") to generate a URL for route "user".');
1104+
1105+
$this->getGenerator($routes)->generate('user', [
1106+
'_query' => [
1107+
'id' => '456',
1108+
],
1109+
]);
1110+
}
1111+
1112+
/**
1113+
* @group legacy
1114+
*/
1115+
public function testQueryParametersWithScalarValue()
1116+
{
1117+
$routes = $this->getRoutes('user', new Route('/user/{id}'));
1118+
1119+
$this->expectDeprecation(
1120+
'Since symfony/routing 7.4: Parameter "_query" is reserved for passing an array of query parameters. ' .
1121+
'Passing a scalar value is deprecated and will throw an exception in Symfony 8.0.',
1122+
);
1123+
1124+
$url = $this->getGenerator($routes)->generate('user', [
1125+
'id' => '123',
1126+
'_query' => 'foo',
1127+
]);
1128+
$this->assertSame('/app.php/user/123?_query=foo', $url);
1129+
}
1130+
10571131
protected function getGenerator(RouteCollection $routes, array $parameters = [], $logger = null, ?string $defaultLocale = null)
10581132
{
10591133
$context = new RequestContext('/app.php');

0 commit comments

Comments
 (0)