16

Is there any way to add an optional parameter in the middle of a route ?

Example routes:

/things/entities/
/things/1/entities/

I tried this, but it does not work:

get('things/{id?}/entities', 'MyController@doSomething');

I know I can do this...

get('things/entities', 'MyController@doSomething');
get('things/{id}/entities', 'MyController@doSomething');

... but my question is: Can I add an optional parameter in the middle of a route?

3 Answers 3

7

No. Optional parameters need to go to the end of the route, otherwise Router wouldn't know how to match URLs to routes. What you implemented already is the correct way of doing that:

get('things/entities', 'MyController@doSomething');
get('things/{id}/entities', 'MyController@doSomething');

You could try doing it with one route:

get('things/{id}/entities', 'MyController@doSomething');

and pass * or 0 if you want to fetch entities for all things, but I'd call it a hack.

There are some other hacks that could allow you to use one route for that, but it will increase the complexity of your code and it's really not worth it.

Sign up to request clarification or add additional context in comments.

Comments

2

Having the optional parameter in the middle of the route definition like that, will only work when the parameter is present. Consider the following:

  • when accessing the path things/1/entities, the id parameter will correctly get the value of 1.
  • when accessing the path things/entities, because Laravel uses regular expressions that match from left to right, the entities part of the path will be considered to be the value of the id parameter (so in this case $id = 'entitites';). This means that the router will not actually be able to match the full route, because the id was matched and it now expects to have a trailing /entities string as well (so the route that would match would need to be things/entities/entities, which is of course not what we're after).

So you'll have to go with the separate route definition approach.

Comments

1

The "correct" answer for this question is; No, you can't and shouldn't use an optional parameter unless it's at the end of the route/url.

But, if you absolutely need to have an optional parameter in the middle of a route, there is a way to achieve that. It's not a solution I would recommend, but here you go:

routes/web.php:

// Notice the missing slash
Route::get('/test/{id?}entities', 'Pages\TestController@first')
    ->where('id', '([0-9/]+)?')
    ->name('first');

Route::get('/test/entities', 'Pages\TestController@second')
    ->name('second');

app/Http/Controllers/Pages/TestController.php:

<?php

namespace App\Http\Controllers\Pages;

use App\Http\Controllers\Controller;

class TestController extends Controller
{
    public function first($id = null)
    {
        // If $id is not null, it will have a trailing slash
        $id = rtrim($id, '/');

        return 'First route with: ' . $id;
    }

    public function second()
    {
        return 'Second route';
    }
}

resources/views/test.blade.php:

<!-- Notice the trailing slash on id -->
<!-- Will output http://myapp.test/test/123/entities -->
<a href="{{ route('first', ['id' => '123/']) }}">
    First route
</a>

<!-- Will output http://myapp.test/test/entities -->
<a href="{{ route('second') }}">
    Second route
</a>

Both links will trigger the first-method in TestController. The second-method will never be triggered.

This solution works in Laravel 6.3, not sure about other versions. And once again, this solution is not good practice.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.