Advanced Laravel route parameters casting.
Hello, everyone!
Most of the laravel developers have used Explicit Binding at least once. When we speak about explicit model binding we mean convention based model resolution, you can define how route parameters correspond with models.
// RouteServiceProvider.phppublic function boot()
{
Route::model('user', User::class);// orRoute::bind('user', function ($value) {
return User::where('name', $value)->firstOrFail();
});// web.phpRoute::get('/users/{user}', function (User $user) {
//
});
99% of the laravel developers use Route Model Binding every day. Implicit Binding is — automatically resolving Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name.
// web.phpRoute::get('/users/{user}', function (User $user) {
// ...
});// orRoute::get('/users/{user}', 'UserController@show');// UserController.phppublic function show(User $user)
{
// ...
}
In case of Explicit Binding everything is clear, because we explicitly bind specific parameter with a model or a closure. But what about Implicit Binding?
Let’s look at this in more detail.
Implicit Binding
The name speaks for itself. A developer simply specifies model type for route parameter or controller action and the router automatically resolves Eloquent models.
How does this happen?
The starting point for this process is middleware — \Illuminate\Routing\Middleware\SubstituteBindings
.
It has two purposes:
- Custom binding registration through
Route:bind
andRoute::model
- Looking for route parameters that implementing
\Illuminate\Contracts\Routing\UrlRoutable
interface and then resolving these. If you look at the Eloquent class, you’ll see that the class implements the interface.
The main purpose of the middleware is to look for route parameters with the interface and replace them with Eloquent models.
Below you’ll find the vary basic representation of the middleware function
You can find the full code of the middleware here: https://github.com/laravel/framework/blob/8.x/src/Illuminate/Routing/ImplicitRouteBinding.php#L21
Once we understand how it works we can create our own implementation of the route parameters resolving.
Custom solution
Let’s imagine that we use uuid
instead of auto increment ids. The first thing we want to do is to validate a route parameter and cast it to Ramsey\Uuid\UuidInterface
.
Here are the route and controller examples
// web.php
Route::get('user/{user}', 'UserController@show')// UserController.php
public function show(\Ramsey\Uuid\UuidInterface $user)
{
// ...
}
And we want to make a simple request
GET http://site.com/user/123e4567-e89b-12d3-a456-426614174000
and cast the uuid to Ramsey\Uuid\UuidInterface
interface, something like this:
// Creates a UUID instance from a string UUID with automatically string validation
$uuid = \Ramsey\Uuid\Uuid::fromString(...);
The simplest way is to use Route:bind
function. You can see an example below
But there is a huge disadvantage: what if we don’t want to use uuid
or userUuid
parameter names? We have to remember all the parameter names and .
We can do the route parameters binding in a better way!
What if we automatically cast all the route parameters, type-hinted with Ramsey\Uuid\UuidInterface
to an object that resolves the interface.
Let’s try to do it.
According to implicit binding we explored above, I like the idea doing this using middleware and then use the middleware everywhere we want. (by analogy with \Illuminate\Routing\Middleware\SubstituteBindings
middleware)
You can see the full code of the middleware below
And then we register the middleware in App\Http\Kernel.php
. Something like this
protected $middlewareGroups = [
'web' => [
...
\App\Interfaces\Http\Middleware\SubstituteUuids::class,
], 'api' => [
...
],
];
And the laravel framework will automatically take care of the route parameters casting.
Thank you!
If you liked this article, let me know here :)