Reputation: 352
I have a site which is localized into several languages. Every public route is prefixed with the locale key (e.g. /{locale}/foo/bar
), which gets caught and applied by middleware.
When generating URLs to point to other pages, I end up needing to feed the current locale into every url, like so:
<a href={{ route('foo.bar', ['locale' => app()->getLocale()]) }}">Foo Bar</a>
Otherwise the output url will contain %7Blocale%7D
, which breaks it. This strikes me as needlessly verbose. Is there not a way to specify a default value for a given named parameter, such that if no value is explicitly provided for 'locale'
it can be defaulted to whatever the current locale is?
I've inspected the UrlGenerator class, but I don't see anything to that effect.
The Route class has a defaults property, but that only appears to be used as part of binding the route to the current request.
Ultimately, not a huge issue, just wondering if anyone has any ideas for ways to save a bit of sanity.
Upvotes: 2
Views: 923
Reputation: 11
You can use URL defaults as well at a middleware:
use Illuminate\Support\Facades\URL;
URL::defaults(
[
'locale' => $locale
]
);
Upvotes: 1
Reputation: 352
There isn't any built in means of doing this, but I managed to achieve the desired result by extending the UrlGenerator
<?php
namespace App\Services;
use Illuminate\Routing\UrlGenerator as BaseGenerator;
use Illuminate\Support\Arr;
class UrlGenerator extends BaseGenerator
{
protected $default_parameters = [];
public function setDefaultParameter($key, $value){
$this->default_parameters[$key] = $value;
}
public function removeDefaultParameter($key){
unset($this->default_parameters[$key]);
}
public function getDefaultParameter($key){
return isset($this->default_parameters[$key]) ? $this->default_parameters[$key] : null;
}
protected function replaceRouteParameters($path, array &$parameters)
{
if (count($parameters) || count($this->default_parameters)) {
$path = preg_replace_sub(
'/\{.*?\}/', $parameters, $this->replaceNamedParameters($path, $parameters)
);
}
return trim(preg_replace('/\{.*?\?\}/', '', $path), '/');
}
protected function replaceNamedParameters($path, &$parameters)
{
return preg_replace_callback('/\{(.*?)\??\}/', function ($m) use (&$parameters) {
return isset($parameters[$m[1]]) ? Arr::pull($parameters, $m[1]) : ($this->getDefaultParameter($m[1]) ?: $m[0]);
}, $path);
}
}
Then rebinding our subclass into the service container
class RouteServiceProvider extends ServiceProvider
{
public function register(){
parent::register();
//bind our own UrlGenerator
$this->app['url'] = $this->app->share(function ($app) {
$routes = $app['router']->getRoutes();
$url = new UrlGenerator(
$routes, $app->rebinding(
'request', function ($app, $request) {
$app['url']->setRequest($request);
}
)
);
$url->setSessionResolver(function () {
return $this->app['session'];
});
$app->rebinding('routes', function ($app, $routes) {
$app['url']->setRoutes($routes);
});
return $url;
});
}
//...
}
Then all I needed to do was inject the default locale into the UrlGenerator from the Locale middleware
public function handle($request, Closure $next, $locale = null) {
//...
$this->app['url']->setDefaultParameter('locale', $locale);
return $next($request);
}
Now route('foo.bar')
will automatically bind the current locale to the route, unless another is explicitly provided.
Upvotes: 0
Reputation: 1829
When you define your routes, use optional variables with defaults:
Routes:
Route::get('{locale?}/foo/bar', 'Controller@fooBar');
Controller:
public function __construct()
{
$this->locale = session()->has('locale') ? session('locale') : 'en';
}
public function fooBar($locale = null)
{
$locale = $locale ?: $this->locale;
}
OR:
public function fooBar($locale = 'en')
{
$locale = $locale ?: $this->locale;
}
Wherever you call your route:
<a href="{{ route('foo.bar', null) }}">Foo Bar</a>
You could optionally put the constructor in a BaseController class that all your other controllers extend.
There may be better ways to do this, but this would keep you from having to include the locale wherever you call a route.
Upvotes: 0