Skip to content

Commit 8667b83

Browse files
committed
Enable use of Route::localizedUrl() with unnamed routes
1 parent e28501d commit 8667b83

File tree

2 files changed

+181
-11
lines changed

2 files changed

+181
-11
lines changed

src/LocalizedUrlGenerator.php

Lines changed: 105 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace CodeZero\LocalizedRoutes;
44

5+
use Illuminate\Database\Eloquent\Model;
56
use Illuminate\Support\Collection;
67
use Illuminate\Support\Facades\App;
78
use Illuminate\Support\Facades\Config;
@@ -45,7 +46,7 @@ public function __construct()
4546
*/
4647
public function generateFromRequest($locale = null, $parameters = null, $absolute = true)
4748
{
48-
return ($this->isDefault404() || $this->isNonLocalizedFallback404())
49+
return ($this->isDefault404() || $this->isNonLocalizedFallback404() || ! $this->routeHasName())
4950
? $this->generateFromUrl($locale, $parameters, $absolute)
5051
: $this->generateFromRoute($locale, $parameters, $absolute);
5152
}
@@ -113,6 +114,10 @@ protected function generateFromUrl($locale = null, $parameters = null, $absolute
113114
// or use the current host by default.
114115
$urlParts['host'] = $domains[$locale] ?? $urlParts['host'];
115116

117+
if ($this->routeExists() && ! $this->route->isFallback) {
118+
$urlParts['path'] = $this->replaceParameters($locale, $this->route->uri(), $this->prepareParameters($locale, $parameters));
119+
}
120+
116121
if (empty($domains)) {
117122
// Localize the path if no custom domains are configured.
118123
$currentPath = $urlParts['path'] ?? '';
@@ -137,16 +142,7 @@ protected function generateFromRoute($locale = null, $parameters = null, $absolu
137142
return URL::current();
138143
}
139144

140-
$parameters = $parameters ?: $this->route->parameters();
141-
$model = Collection::make($parameters)->first();
142-
143-
if ($model instanceof ProvidesRouteParameters) {
144-
$parameters = $model->getRouteParameters($locale);
145-
}
146-
147-
if (is_callable($parameters)) {
148-
$parameters = $parameters($locale);
149-
}
145+
$parameters = $this->prepareParameters($locale, $parameters);
150146

151147
return route($this->route->getName(), $parameters, $absolute, $locale);
152148
}
@@ -261,4 +257,102 @@ protected function getSupportedLocales()
261257
{
262258
return Config::get('localized-routes.supported-locales', []);
263259
}
260+
261+
/**
262+
* Check if the route has a name.
263+
*
264+
* @return bool
265+
*/
266+
protected function routeHasName()
267+
{
268+
return $this->routeExists() && $this->stripLocaleFromRouteName($this->route->getName()) !== '';
269+
}
270+
271+
/**
272+
* Strip the locale from the beginning of a route name.
273+
*
274+
* @param string $name
275+
*
276+
* @return string
277+
*/
278+
protected function stripLocaleFromRouteName($name)
279+
{
280+
$parts = explode('.', $name);
281+
282+
// If there is no dot in the route name,
283+
// there is no locale in the route name.
284+
if (count($parts) === 1) {
285+
return $name;
286+
}
287+
288+
$locales = $this->getLocaleKeys();
289+
290+
// If the first part of the route name is a valid
291+
// locale, then remove it from the array.
292+
if (in_array($parts[0], $locales)) {
293+
array_shift($parts);
294+
}
295+
296+
// Rebuild the normalized route name.
297+
$name = join('.', $parts);
298+
299+
return $name;
300+
}
301+
302+
/**
303+
* Prepare any route parameters.
304+
*
305+
* @param string $locale
306+
* @param mixed $parameters
307+
*
308+
* @return array
309+
*/
310+
protected function prepareParameters($locale, $parameters)
311+
{
312+
if ($this->routeExists() && $parameters === null) {
313+
$parameters = $this->route->parameters();
314+
}
315+
316+
$model = Collection::make($parameters)->first();
317+
318+
if ($model instanceof ProvidesRouteParameters) {
319+
$parameters = $model->getRouteParameters($locale);
320+
}
321+
322+
if (is_callable($parameters)) {
323+
$parameters = $parameters($locale);
324+
}
325+
326+
return $parameters;
327+
}
328+
329+
/**
330+
* Replace parameter placeholders with their value.
331+
*
332+
* @param string $locale
333+
* @param string $uri
334+
* @param array $parameters
335+
*
336+
* @return string
337+
*/
338+
protected function replaceParameters($locale, $uri, array $parameters)
339+
{
340+
preg_match_all('/{([a-z_.-]+)}/', $uri, $matches);
341+
$paramKeys = $matches[1] ?? [];
342+
343+
foreach ($paramKeys as $index => $key) {
344+
$value = $parameters[$key] ?? $parameters[$index];
345+
346+
if ($value instanceof Model) {
347+
$originalLocale = App::getLocale();
348+
App::setLocale($locale);
349+
$value = $value->getRouteKey();
350+
App::setLocale($originalLocale);
351+
}
352+
353+
$uri = str_replace("{{$key}}", $value, $uri);
354+
}
355+
356+
return $uri;
357+
}
264358
}

tests/Unit/Macros/LocalizedUrlMacroTest.php

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,82 @@ public function you_can_pass_it_a_closure_that_returns_the_parameters_without_ro
184184
], $response->original);
185185
}
186186

187+
/** @test */
188+
public function it_handles_unnamed_non_localized_routes()
189+
{
190+
$this->setSupportedLocales(['en', 'nl']);
191+
192+
Route::get('route/one', function () {
193+
return [
194+
'current' => Route::localizedUrl(),
195+
'en' => Route::localizedUrl('en'),
196+
'nl' => Route::localizedUrl('nl'),
197+
];
198+
});
199+
Route::get('route/two', function () {
200+
return [
201+
'current' => Route::localizedUrl(),
202+
'en' => Route::localizedUrl('en'),
203+
'nl' => Route::localizedUrl('nl'),
204+
];
205+
});
206+
207+
$response = $this->call('GET', '/route/one');
208+
$response->assertOk();
209+
$this->assertEquals([
210+
'current' => url('/route/one'),
211+
'en' => url('/route/one'),
212+
'nl' => url('/route/one'),
213+
], $response->original);
214+
215+
$response = $this->call('GET', '/route/two');
216+
$response->assertOk();
217+
$this->assertEquals([
218+
'current' => url('/route/two'),
219+
'en' => url('/route/two'),
220+
'nl' => url('/route/two'),
221+
], $response->original);
222+
}
223+
224+
/** @test */
225+
public function it_handles_unnamed_localized_routes()
226+
{
227+
$this->setSupportedLocales(['en', 'nl']);
228+
229+
Route::localized(function () {
230+
Route::get('route/one', function () {
231+
return [
232+
'current' => Route::localizedUrl(),
233+
'en' => Route::localizedUrl('en'),
234+
'nl' => Route::localizedUrl('nl'),
235+
];
236+
});
237+
Route::get('route/two', function () {
238+
return [
239+
'current' => Route::localizedUrl(),
240+
'en' => Route::localizedUrl('en'),
241+
'nl' => Route::localizedUrl('nl'),
242+
];
243+
});
244+
});
245+
246+
$response = $this->call('GET', '/en/route/one');
247+
$response->assertOk();
248+
$this->assertEquals([
249+
'current' => url('/en/route/one'),
250+
'en' => url('/en/route/one'),
251+
'nl' => url('/nl/route/one'),
252+
], $response->original);
253+
254+
$response = $this->call('GET', '/en/route/two');
255+
$response->assertOk();
256+
$this->assertEquals([
257+
'current' => url('/en/route/two'),
258+
'en' => url('/en/route/two'),
259+
'nl' => url('/nl/route/two'),
260+
], $response->original);
261+
}
262+
187263
/** @test */
188264
public function it_returns_the_current_url_for_existing_non_localized_routes()
189265
{

0 commit comments

Comments
 (0)