2

I am developing a Multilingual application and trying to make a middleware to pass route {locale} prefix to URL. But now I do not need to use this {locale} parameter in controller, for example:

public function getPost(App\Post $post)
{
    return view('welcome')->withPost($post);
}

But the code above does not work unless I change the App\Post $post to $locale, App\Post $post.

The problem is because I'll need to pass the $locale parameter whenever I create a new controller. That is not cool.

How to avoid passing $locale parameter to all controllers? I do not need it because I already used it on my middleware.

UPDATE:

routes\web.php

Route::prefix('{locale}')->middleware('locale')->group(function () {     

    Route::get('/', 'PageController@getHome')->name('welcome');
    Auth::routes();
    Route::get('/home', 'HomeController@index')->name('home');

    ...

    // This route must be the last!
    Route::get('/{post}', 'PageController@getPost')->name('post');
});
Cœur
  • 37,241
  • 25
  • 195
  • 267
Renan Coelho
  • 1,360
  • 2
  • 20
  • 30

3 Answers3

16

There is a forgetParameter()-method in Laravel's route class, that can remove a parameter from being parsed to controller methods.

It can be used e.g. in a middleware, by calling it like so:

$request->route()->forgetParameter('locale');

Then the parameter will be removed from the controller dispatcher's parameter property and thereby not get parsed on to the controller methods as parameter.

thephper
  • 2,342
  • 22
  • 21
  • A bit stupid that we have to forget the parameter while laravel could have passed it after all the requested parameters instead of before.. So you'd have this `App\Post $post, $locale` which keeps declaration compatible. Too late for that now :( – Tofandel Jul 29 '20 at 17:26
  • 1
    You can use an inline middleware in a parent Controller for specific namespace, in the constructor: `$this->middleware(function ($request, $next) { /* retrieve the value a set to a controller property ... */; $request->route()->forgetParameter('locale'); }`. – nelson6e65 Oct 13 '21 at 23:06
2

I solved my question without using a prefix parameter, but using a custom helper function.

By this way, I do not need a middleware to parse the {locale} prefix parameter anymore. See my custom helper function to parse locale from URL:

function parseLocale()
{
    $locale = Request::segment(1);
    $languages = ['pt', 'it', 'fr', 'ru', 'es'];

    if (in_array($locale, $languages)) {
        App::setLocale($locale);
        return $locale;
    }

    return '/';
}

Now I need just to use it on my Route::prefix() like following:

Route::prefix(parseLocale())->group(function () {
    Auth::routes();
    Route::get('home', 'HomeController@index')->name('home');
    Route::get('/', 'PageController@getHome')->name('welcome');
    ...

As you can see, if you try to navigate into www.site.com/pt/something the application will give you the something route the same way you try to navigate into www.site.com/something. But without the locale prefix the Laravel will load the default language you've set in config\app.php.

Thank you guys!

Renan Coelho
  • 1,360
  • 2
  • 20
  • 30
0

Remove {locale} from your route parameters if it is not needed. It sounds like you still have the routes defined with it in there. Please post your routes/web.php so we can verify.

If your route is defined like:

Route::get('/{locale}/{post}', 'PostController@getPost');

Laravel will expect 2 parameters and bind whatever is in place for the first one. So for example, '/some-post' would bind 'some-post' to {locale} and still expect another route parameter for {post}

  • I've updated. As you can see, I 'm using a {locale} prefix for every routes. Is there a way to leave the `locale` parameter optional? I need to navigate into `www.site.com/fr/about` and `www.site.com/about`. If not passing `locale` parameter, the english language is loaded by default; – Renan Coelho Jun 18 '17 at 23:06
  • You can't as far as I know, that makes the URL ambiguous and the router isn't able to distinguish optional parameters in the middle of a URL. If `{locale}` were the last parameter in the list, then you could mark it optional, but not with the structure you have in place. –  Jun 18 '17 at 23:26
  • Yeah! So... where to go at this point to solve the question? If there is another way/structure/algorithm I 'll appreciate. – Renan Coelho Jun 18 '17 at 23:32
  • You may be able to come up with something using regex parameter matching. Say if the country code is only ever two characters followed by a `{post}` parameter. That way `www.site.com/fr/about` would pass and `www.site.com/about` would fail. That's really the only approach I can think of at them moment. https://laravel.com/docs/5.4/routing#parameters-regular-expression-constraints –  Jun 18 '17 at 23:37