Skip to content

[Routing] Allow query-specific parameters in UrlGenerator using _query #60508

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: 7.4
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/Symfony/Component/Routing/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
CHANGELOG
=========

7.4
---

* Allow query-specific parameters in `UrlGenerator` using `_query`

7.3
---

Expand Down
13 changes: 13 additions & 0 deletions src/Symfony/Component/Routing/Generator/UrlGenerator.php
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,18 @@ public function generate(string $name, array $parameters = [], int $referenceTyp
*/
protected function doGenerate(array $variables, array $defaults, array $requirements, array $tokens, array $parameters, string $name, int $referenceType, array $hostTokens, array $requiredSchemes = []): string
{
$queryParameters = [];

if (isset($parameters['_query'])) {
if (\is_array($parameters['_query'])) {
$queryParameters = $parameters['_query'];
unset($parameters['_query']);
} else {
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.');
// throw new InvalidParameterException('Parameter "_query" must be an array of query parameters.');
}
}

$variables = array_flip($variables);
$mergedParams = array_replace($defaults, $this->context->getParameters(), $parameters);

Expand Down Expand Up @@ -260,6 +272,7 @@ protected function doGenerate(array $variables, array $defaults, array $requirem

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

array_walk_recursive($extra, $caster = static function (&$v) use (&$caster) {
if (\is_object($v)) {
Expand Down
74 changes: 74 additions & 0 deletions src/Symfony/Component/Routing/Tests/Generator/UrlGeneratorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -1054,6 +1054,80 @@ public function testUtf8VarName()
$this->assertSame('/app.php/foo/baz', $this->getGenerator($routes)->generate('test', ['bär' => 'baz']));
}

public function testQueryParameters()
{
$routes = $this->getRoutes('user', new Route('/user/{username}'));
$url = $this->getGenerator($routes)->generate('user', [
'username' => 'john',
'a' => 'foo',
'b' => 'bar',
'c' => 'baz',
'_query' => [
'a' => '123',
'd' => '789',
],
]);
$this->assertSame('/app.php/user/john?a=123&b=bar&c=baz&d=789', $url);
}

public function testRouteHostParameterAndQueryParameterWithSameName()
{
$routes = $this->getRoutes('admin_stats', new Route('/admin/stats', requirements: ['domain' => '.+'], host: '{siteCode}.{domain}'));
$url = $this->getGenerator($routes)->generate('admin_stats', [
'siteCode' => 'fr',
'domain' => 'example.com',
'_query' => [
'siteCode' => 'us',
],
], UrlGeneratorInterface::NETWORK_PATH);
$this->assertSame('//fr.example.com/app.php/admin/stats?siteCode=us', $url);
}

public function testRoutePathParameterAndQueryParameterWithSameName()
{
$routes = $this->getRoutes('user', new Route('/user/{id}'));
$url = $this->getGenerator($routes)->generate('user', [
'id' => '123',
'_query' => [
'id' => '456',
],
]);
$this->assertSame('/app.php/user/123?id=456', $url);
}

public function testQueryParameterCannotSubstituteRouteParameter()
{
$routes = $this->getRoutes('user', new Route('/user/{id}'));

$this->expectException(MissingMandatoryParametersException::class);
$this->expectExceptionMessage('Some mandatory parameters are missing ("id") to generate a URL for route "user".');

$this->getGenerator($routes)->generate('user', [
'_query' => [
'id' => '456',
],
]);
}

/**
* @group legacy
*/
public function testQueryParametersWithScalarValue()
{
$routes = $this->getRoutes('user', new Route('/user/{id}'));

$this->expectDeprecation(
'Since 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.',
);

$url = $this->getGenerator($routes)->generate('user', [
'id' => '123',
'_query' => 'foo',
]);
$this->assertSame('/app.php/user/123?_query=foo', $url);
}

protected function getGenerator(RouteCollection $routes, array $parameters = [], $logger = null, ?string $defaultLocale = null)
{
$context = new RequestContext('/app.php');
Expand Down
Loading