Route::is() and Route:has()
Hello, I suggest to implement Route::is() and Route::has() to check the current route.
Actually I did a workaround:
Route::is('login') became Route::is(app()->getLocale() . '.login')
Route::has('register') became Route::has(app()->getLocale() . '.register')
But this is no nice. Thanks for your work
Hi, I will look into it, seems like a cool idea! I'm currently doing a similar thing:
Route::is('*.blog.*')
Yes, I didn't thought to use "*" lol
Just a quick first thought: maybe it's better to include a macro like Route::isLocalized($name, $locale = null) and Route::hasLocalized($name, $locale = null). Else it won't be possible to check for a specific route.
Mmh I'm not sure, actually I have for example a blade like this
<div class="card-header">{{ Route::is(app()->getLocale() . '.post.edit') ? __('message.edit') : __('message.new') }} Post</div>
And using this it shows correctly each localization message "edit" or "new" depending of its route
But maybe I was missunderstanding and you was talking about how implement it
Yeah, so that could become:
<div class="card-header">{{ Route::isLocalized('post.edit') ? __('message.edit') : __('message.new') }} Post</div>
Maybe it is possible to override Route::is() and Route::has(), but I'll have to check for any implications this might have. It does look nicer.
So then if you have a route named en.post.edit, calling Route::is('post.edit') and Route::has('post.edit') would return true. This kinda has the same limitation as the route() helper in this package, that you should avoid using the same name for localized and non-localized routes.
I could also add an optional parameter to check against a specific locale. If $locale is false, it could do an exact route check (non-localized):
Route::is('post.edit'); // true - checking non-localized and any locale
Route::is('post.edit', $locale = 'en'); // true - checking only 'en' locale
Route::is('post.edit', $locale = false); // false - checking only non-localized
Same idea for Route::has():
Route::has('post.edit'); // true - checking non-localized and any locale
Route::has('post.edit', $locale = 'en'); // true - checking only 'en' locale
Route::has('post.edit', $locale = false); // false - checking only non-localized
Partially implemented by #33 but still needs testing.
Hi. After multiple tests in my project, I can't get it to work. It seems the result is always true. Furthermore, it does not support an array of routes names as parameters like the original Laravel method.
I've been able to successfully get it working using this macro:
Route::macro('isLocalized', function ($patterns, $locale = '*') {
$names = collect($patterns)->map(function ($name) use ($locale) {
return $locale . '.' . $name;
})->all();
return Route::is($names);
});
This technique supports single and array parameters and has the advantage to use the original Laravel method. You can pass a specific locale if needed. If it may help. I'm happy to submit a PR if needed.
Cheers,
Hi
That seems like a great solution.
PR is always welcome.
I will probably rename the other macro to hasLocalized() (deprecating localizedHas() until the next major version).
I still hope to use a modified version of Route::is() and Route::has() one day, as it really feels like you are just using a default Laravel app. I think that would be cool, but might be tricky. So for now custom macro's will do :)
Thanks!
@ivanvermeyen can you release this changes?
The website is crashing whenever I use an object that is missing at least one localized version...
I wrote some tests to verify everything works as expected and implemented a solution based on @AlexisSerneels example.
You can now:
Check if the current route is localized:
Route::isLocalized();
Check if the name of the current localized route matches a specific name in any locale:
Route::isLocalized('route-name');
Check if the name of the current localized route matches a specific name in a specific locale:
Route::isLocalized('route-name', 'en');
Check if the name of the current localized route matches any of the given names in any locale:
Route::isLocalized(['route-one', 'route-two']);
Check if the name of the current localized route matches any of the given names in a specific locale:
Route::isLocalized(['route-one', 'route-two'], 'en');
Check if the name of the current localized route matches the given names and locales:
Route::isLocalized('route-name', ['en', 'nl']);
Route::isLocalized(['route-one', 'route-two'], ['en', 'nl']);
Will tag and release when CI tests have passed 👍
@ivanvermeyen what about a function or check for Route::localizedUrl?
I can't use your original function, because if I try to get a localizedURL of an inexistent route, it will crash my whole website.
Perhaps: Route::hasLocalizedURL?
@sheinfeld Can you give an example of what you are doing exactly? I tried hitting 404's on my project with and without the fallback route registered, and it always shows the 404 page as expected. Without the fallback route there is no localization of course. It would just show the current URL.
Can you check if there is anything in your app's exception handler? I remember I had an issue until I removed these lines (but I think I added those myself at some point):
$this->renderable(function (NotFoundHttpException $e, Request $request) {
return Route::respondWithRoute('fallback');
});
$this->renderable(function (ModelNotFoundException $e, Request $request) {
return Route::respondWithRoute('fallback');
});
@ivanvermeyen we merged articles from different localized platforms, so only need new content is translated in all locales.
This is an example:
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="languageButton">
@foreach (config('constants.locales') as $lang => $language)
@if ($lang != app()->getLocale())
<a class="dropdown-item"
href="{{ isset($article) && $article->hasTranslation($lang) ? Route::currentLocalizedUrl($lang) : (!isset($article) ?: Route::currentLocalizedUrl($lang)) }}"
hreflang="{{ $lang }}">
<span
class="flag-icon flag-icon-{{ $lang == "en" ? 'gb' : $lang }} mr-1"></span>{{$language}}
</a>
@endif
@endforeach
</div>
```
@sheinfeld What does this macro do? Route::currentLocalizedUrl($lang)
This is my switcher:
<ul class="flex list-none">
@foreach(Config::get('localized-routes.supported-locales') as $locale)
@if ( ! App::isLocale($locale))
<li class="first:border-r border-gray-400">
<a href="{{ Route::localizedUrl($locale) }}"
class="block px-4 text-sm text-gray-400 hover:text-gray-100 font-bold tracking-wider"
>
{{ strtoupper($locale) }}
</a>
</li>
@endif
@endforeach
</ul>
@sheinfeld I'm not sure if this can throw exceptions, but in your href:
isset($article) && $article->hasTranslation($lang)
? Route::currentLocalizedUrl($lang)
: ( ! isset($article) ?: Route::currentLocalizedUrl($lang) ) //=> if article is not set, the href is true ?
I probably should have asked earlier, but what exception do you get?
@ivanvermeyen that macro, currentLocalizedUrl, was taken from a stackoverflow post If I'm not mistaken:
Route::macro('currentLocalizedUrl', function ($locale = null, $parameters = null, $absolute = true) {
$locale = $locale ?? app()->getLocale();
$parameters = $parameters ?: Route::current()->parameters();
$currentLocale = app()->getLocale();
app()->setLocale($locale);
foreach ($parameters as $attribute => $value) {
if ($value instanceof Model) {
$parameters[$attribute] = $value->getRouteKey();
}
}
app()->setLocale($currentLocale);
return route(Route::current()->getName(), $parameters, $absolute, $locale);
});
@ivanvermeyen your macro localizedUrl, returns 1 whenever the route doesn't exist :(
@sheinfeld If you look at your href logic, it can return true if $article is not set, which is 1 when converted into a string.
@ivanvermeyen oh, I see... How would you do it then?
@sheinfeld it seems as long as $article is set, you return Route::currentLocalizedUrl($lang), both when it has a translation and not. So you can probably simplify this:
href="{{ isset($article) ? Route::currentLocalizedUrl($lang) : 'whatever you want to show when $article is not set' }}"