laravel-localized-routes icon indicating copy to clipboard operation
laravel-localized-routes copied to clipboard

Route::is() and Route:has()

Open FedericoHeichou opened this issue 5 years ago • 21 comments

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

FedericoHeichou avatar Mar 07 '20 13:03 FedericoHeichou

Hi, I will look into it, seems like a cool idea! I'm currently doing a similar thing:

Route::is('*.blog.*')

ivanvermeyen avatar Mar 07 '20 13:03 ivanvermeyen

Yes, I didn't thought to use "*" lol

FedericoHeichou avatar Mar 07 '20 13:03 FedericoHeichou

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.

ivanvermeyen avatar Mar 07 '20 13:03 ivanvermeyen

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

FedericoHeichou avatar Mar 07 '20 13:03 FedericoHeichou

But maybe I was missunderstanding and you was talking about how implement it

FedericoHeichou avatar Mar 07 '20 13:03 FedericoHeichou

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

ivanvermeyen avatar Mar 07 '20 14:03 ivanvermeyen

Partially implemented by #33 but still needs testing.

ivanvermeyen avatar Nov 02 '20 09:11 ivanvermeyen

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,

AlexisSerneels avatar Dec 08 '20 09:12 AlexisSerneels

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 avatar Dec 08 '20 12:12 ivanvermeyen

@ivanvermeyen can you release this changes?

The website is crashing whenever I use an object that is missing at least one localized version...

sheinfeld avatar Aug 16 '21 08:08 sheinfeld

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 avatar Aug 16 '21 14:08 ivanvermeyen

@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 avatar Aug 17 '21 09:08 sheinfeld

@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 avatar Aug 17 '21 10:08 ivanvermeyen

@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 avatar Aug 17 '21 11:08 sheinfeld

@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>

ivanvermeyen avatar Aug 17 '21 11:08 ivanvermeyen

@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 avatar Aug 17 '21 13:08 ivanvermeyen

@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);
        });

sheinfeld avatar Aug 18 '21 15:08 sheinfeld

@ivanvermeyen your macro localizedUrl, returns 1 whenever the route doesn't exist :(

sheinfeld avatar Aug 18 '21 15:08 sheinfeld

@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 avatar Aug 18 '21 15:08 ivanvermeyen

@ivanvermeyen oh, I see... How would you do it then?

sheinfeld avatar Aug 18 '21 15:08 sheinfeld

@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' }}"

ivanvermeyen avatar Aug 18 '21 15:08 ivanvermeyen