Skip to content

Commit fbd34fe

Browse files
committed
Accept query string parameters via Route::localizedUrl()
1 parent 37bc95c commit fbd34fe

File tree

3 files changed

+117
-11
lines changed

3 files changed

+117
-11
lines changed

src/LocalizedUrlGenerator.php

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -48,16 +48,29 @@ public function generateFromRequest($locale = null, $parameters = null, $absolut
4848
{
4949
$urlBuilder = UrlBuilder::make(Request::fullUrl());
5050
$locale = $locale ?: $this->detectLocale($urlBuilder);
51-
$parameters = $this->prepareParameters($locale, $parameters ?: $this->getRouteParameters());
5251

53-
if ($url = $this->generateFromNamedRoute($locale, $parameters, $absolute)) {
54-
return $url . $urlBuilder->getQueryString();
55-
}
52+
// $parameters can be an array, a function or it can contain model instances!
53+
// Normalize the parameters so we end up with an array of key => value pairs.
54+
$parameters = $this->prepareParameters($locale, $parameters ?: $this->getRouteParameters());
5655

5756
if ( ! $this->is404()) {
58-
$urlBuilder->setPath($this->replaceParameters($this->route->uri(), $parameters));
57+
$urlBuilder->setPath($this->route->uri());
58+
59+
list($slugs, $query) = $this->extractQueryParameters($urlBuilder->getPath(), $parameters);
60+
61+
if (count($query)) {
62+
$urlBuilder->setQuery($query);
63+
}
64+
65+
if ($url = $this->generateFromNamedRoute($locale, $parameters, $absolute)) {
66+
return empty($query) ? $url . $urlBuilder->getQueryString() : $url;
67+
}
68+
69+
$urlBuilder->setPath($this->replaceParameters($this->route->uri(), $slugs));
5970
}
6071

72+
// If custom domains are not used and it is not a registered,
73+
// non localized route, update the locale slug in the path.
6174
if ( ! $this->hasCustomDomains() && ($this->is404() || $this->isLocalized())) {
6275
$urlBuilder->setSlugs($this->updateLocaleInSlugs($urlBuilder->getSlugs(), $locale));
6376
}
@@ -284,21 +297,54 @@ protected function updateLocaleInSlugs(array $slugs, $locale)
284297
}
285298

286299
/**
287-
* Replace parameter placeholders with their value.
300+
* Extract URI parameters and query string parameters.
288301
*
289302
* @param string $uri
290303
* @param array $parameters
291304
*
292-
* @return string
305+
* @return array
293306
*/
294-
protected function replaceParameters($uri, $parameters)
307+
protected function extractQueryParameters($uri, $parameters)
295308
{
296309
preg_match_all('/{([a-z_.-]+)}/', $uri, $matches);
297310
$paramKeys = $matches[1] ?? [];
298311

299-
foreach ($paramKeys as $index => $key) {
300-
$value = $parameters[$key] ?? $parameters[$index];
301-
$uri = str_replace("{{$key}}", $value, $uri);
312+
$slugs = [];
313+
$query = [];
314+
$i = 0;
315+
316+
foreach ($parameters as $key => $value) {
317+
// Parameters should be in the same order as the placeholders.
318+
// $key can be a name or an index, so grab the matching key name from the URI.
319+
$paramKey = $paramKeys[$i] ?? null;
320+
321+
// If there is a matching $paramKey,
322+
// we are dealing with a normal parameter,
323+
// else we are dealing with a query string parameter.
324+
if ($paramKey) {
325+
$slugs["{{$paramKey}}"] = $value;
326+
} else {
327+
$query[$key] = $value;
328+
}
329+
330+
$i++;
331+
}
332+
333+
return [$slugs, $query];
334+
}
335+
336+
/**
337+
* Replace parameter placeholders with their value.
338+
*
339+
* @param string $uri
340+
* @param array $parameters
341+
*
342+
* @return string
343+
*/
344+
protected function replaceParameters($uri, $parameters)
345+
{
346+
foreach ($parameters as $placeholder => $value) {
347+
$uri = str_replace($placeholder, $value, $uri);
302348
}
303349

304350
return $uri;

src/UrlBuilder.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,20 @@ public function getQueryString()
131131
return $this->get('query') ? '?' . $this->get('query') : '';
132132
}
133133

134+
/**
135+
* Set the query string parameters.
136+
*
137+
* @param array $query
138+
*
139+
* @return \CodeZero\LocalizedRoutes\UrlBuilder
140+
*/
141+
public function setQuery(array $query)
142+
{
143+
$this->set('query', http_build_query($query));
144+
145+
return $this;
146+
}
147+
134148
/**
135149
* Get the value of a URL part.
136150
*

tests/Unit/Macros/LocalizedUrlMacroTest.php

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -679,6 +679,52 @@ public function it_returns_a_url_with_query_string_for_existing_localized_named_
679679
], $response->original);
680680
}
681681

682+
/** @test */
683+
public function it_accepts_query_string_parameters_using_named_routes()
684+
{
685+
$this->withoutExceptionHandling();
686+
$this->setSupportedLocales(['en', 'nl']);
687+
688+
Route::get('route/{slug}', function () {
689+
return [
690+
'current' => Route::localizedUrl(null, ['another-slug', 'new' => 'value']),
691+
'en' => Route::localizedUrl('en', ['another-slug', 'new' => 'value']),
692+
'nl' => Route::localizedUrl('nl', ['another-slug', 'new' => 'value']),
693+
];
694+
})->name('route');
695+
696+
$response = $this->call('GET', '/route/some-slug?param=value');
697+
$response->assertOk();
698+
$this->assertEquals([
699+
'current' => url('/route/another-slug?new=value'),
700+
'en' => url('/route/another-slug?new=value'),
701+
'nl' => url('/route/another-slug?new=value'),
702+
], $response->original);
703+
}
704+
705+
/** @test */
706+
public function it_accepts_query_string_parameters_using_unnamed_routes()
707+
{
708+
$this->withoutExceptionHandling();
709+
$this->setSupportedLocales(['en', 'nl']);
710+
711+
Route::get('route/{slug}', function () {
712+
return [
713+
'current' => Route::localizedUrl(null, ['another-slug', 'new' => 'value']),
714+
'en' => Route::localizedUrl('en', ['another-slug', 'new' => 'value']),
715+
'nl' => Route::localizedUrl('nl', ['another-slug', 'new' => 'value']),
716+
];
717+
});
718+
719+
$response = $this->call('GET', '/route/some-slug?param=value');
720+
$response->assertOk();
721+
$this->assertEquals([
722+
'current' => url('/route/another-slug?new=value'),
723+
'en' => url('/route/another-slug?new=value'),
724+
'nl' => url('/route/another-slug?new=value'),
725+
], $response->original);
726+
}
727+
682728
/** @test */
683729
public function it_returns_a_url_with_translated_slugs_for_named_routes()
684730
{

0 commit comments

Comments
 (0)