Routes With a Single Parameter

In our previous tutorial, we learned how to define hard-coded routes.

In this tutorial, we will learn how to define routes that can have a single parameter. The parameter is a route's segment whose value can change, depending on what we supply in the GET request. And we want to be able to obtain the value of that segment and use it in our code.

For example, if a page displays a single post from a database, the URL for such page can take any of the following values:

https://www.example.com/posts/1
https://www.example.com/posts/2
https://www.example.com/posts/3

These routes have a segment that can change, and that segment represents the post's id column which can have different values for different posts. We will call this segment a route parameter. To define a route that has one parameter, in our web.php file we write:

<?php

use Illuminate\Support\Facades\Route; // import the Route facade

Route::get('/posts/{id}', function (string $id) { // define a route with an id parameter
    return 'Accessing a post with an id of ' . $id;
});

Here, using the Route::get() function, we define a route that has a single parameter. We name this parameter id and surround it with braces:

'/posts/{id}'

Then, we provide the callback function and declare a single parameter, called $id, which will automatically receive the value of an {id} route parameter.

function (string $id) { 
    return 'Accessing a post with an id of ' . $id;
}

We say the route parameter's value gets automatically passed to/injected into a callback parameter. The URI value of {id} gets automatically injected to a function's argument variable $id.

If we try to access this route and provide a value of 123, for example, we see the following output in our browser:

Single parameter route showing the response based on the id field.

If we access the route with an id value of 456, we observe the following output:

Single parameter route showing a different response, based on the id field.

The name of the function parameter does not have to match the name of the route parameter, it can be something else, like $myId and the function argument will still receive the value of the {id} route parameter.

Note: the route parameter can be declared anywhere in our route. For example, we can have the following routes, and all of them define a single parameter:

https://www.example.com/posts/{id}
https://www.example.com/posts/{id}/comments
https://www.example.com/users/{id}/details
https://www.example.com/some/other/paths/{id}/etc

This route parameter will still be injected to the function argument, regardless of the position inside the route.

Please note that this route parameter definition specifies the required parameter. The parameter must be present in the GET request. If we tried to access the route without supplying a value for an id parameter, as in the following example:

http://127.0.0.1:8000/posts/

We would get a 404 not found error as in the following image:

Single parameter route showing the 404 page not found error.

Optional single parameter

Sometimes, a parameter might or might not be present in the URL. In that case, we want to define an optional parameter by placing the ? character at the end of the route parameter name and we provide a default value for the function argument:

<?php

use Illuminate\Support\Facades\Route; // import the Route facade

// define a route with an optional id parameter
Route::get('/posts/{id?}', function (?string $id = null) { // and set the argument's default value to null
    if (is_null($id)) {
        return 'No id value provided.';
    } else {
        return 'Accessing a post with an id of ' . $id;
    }
});

Since the id route parameter is now optional, we can access the route either with or without the route parameter. If accessing the route without the parameter part:

http://127.0.0.1:8000/posts/

We would observe the following output in our web browser:

A browser showing the result of not providing the value for an optional route parameter.

Use-cases

Using a single parameter to fetch a record by id

We can define a single id parameter route and use it to fetch a record from our database. Our Route::get() function can look like:

Route::get('/posts/{id}', function (string $id) { // define a route with an id parameter
    $myRecord = MyModel::where('id', $id)->firstOrFail();
    // ... other code
});

Note: replace the MyModel with the actual model's name.

This code defines a single id parameter for our route, and uses the parameter's value to fetch a single record using a model class.

Using a single parameter to fetch a record by a slug

If a table has the slug column, and we want to fetch the record using the slug parameter, we define a route accepting a slug and returning a single record:

Route::get('/posts/{slug}', function (string $slug) { // define a route with a slug parameter
    $myRecord = MyModel::where(slug', $slug)->firstOrFail();
    // ... other code
});

Note: replace the MyModel with the actual model's name.

Route parameter constraints

We can apply constraints to our route parameter by chaining the with() method to our Route::get() function call. The with() method accepts the name of the parameter and a regular expression that represents the constraint:

where('id', '[0-9]+')

If we try to access the route with the parameter value that does not match the constraints, for example, with the following address:

http://127.0.0.1:8000/posts/123-bad-id-value

We get a 404 Page Not Found response:

Browser showing the 404 response for a route with an id that does not match the constraint.

To constrain the id parameter, we write:

Route::get('/posts/{id}', function (string $id) { // define a route with an id parameter
    // ... some code
})->where('id', '[0-9]+'); // constrain the id parameter

To constrain the slug parameter, we would use the following code:

Route::get('/posts/{slug}', function (string $slug) { // define a route with a slug parameter
    // ... some code
})->where('slug', '[A-Za-z0-9-_]+'); // constrain the slug parameter

Global parameter constraints

Alternatively, if we want to apply constraints to all the routes, we place one or more Route::pattern('parameter_name', 'constraint_value') function calls inside the boot() method of the application's App\Providers\AppServiceProvider class:

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Route;

class AppServiceProvider extends ServiceProvider
{
    // other code...

    public function boot(): void
    {
        Route::pattern('id', '[0-9]+');
        Route::pattern('slug', '[A-Za-z0-9-_]+');
    }
}

Now, every route that has an id and/or a slug parameter, will apply constraints to those parameters.

Summary

In this tutorial, we learned how to create single parameter routes. In the next tutorial, we will learn how to create multiple parameters routes.