How to Use Route Model Binding in Laravel
A quick guide to setting up and using route model binding for cleaner and more efficient routing.
Introduction
Web applications typically handles which data is been retrieved and how requests are handled through routing. Laravel route model binding is an excellent way to retrieve record from the database by automatically importing Eloquent models into your routes and controllers.
In this article, we’ll explore how Laravel smoothly map route parameters to database records in API or web app which reduces boilerplate code and improve readability.
What is Route Model Binding?
Route model Binding allows you to automatically inject model instances into routes or controllers based on the route parameters. There are two types of Route model Binding supported in Laravel: implicit and explicit binding. For instance, you can pass a post's ID and immediately obtain the post model instance rather than supplying the post's ID and running a database query to acquire the post.
Implicit Model Binding
In Laravel’s implicit model binding which is the simplest common default way to use route model binding, it works by matching the route segment name with the name of the model type-hinted (naming conventions) in the route or controller. Laravel automatically resolves Eloquent models based on route parameters. It assumes the parameter name matches the model's route key (id
by default, or the value returned by the getRouteKeyName()
method).
For example:
//web or api
use App\Models\Post;
Route::get('/posts/{post}', function (Post $post) {
return response()->json($post);
});
When a request is made via /posts/1
, Laravel automatically fetches the post with id = 1
from the database. If the post does not exist, Laravel returns a 404 Not Found response.
Customizing the Key for Route Binding
Sometimes you may wish to resolve Eloquent models using a column other than id
. To do so, you may specify the column in the route parameter definition:
use App\Models\Post;
Route::get('/posts/{post:ulid}', function (Post $post) {
return $post;
});
- Accessing the post using the ulid
However, you can override the default use of id
in Laravel by overriding the getRouteKeyName()
method in the model:
For instance : Bind by slug
or ulid
Instead of id
class Post extends Model
{
public function getRouteKeyName()
{
return 'ulid';
}
}
Use post ulid on the URL instead of id
use App\Models\Post;
Route::get('/posts/{post}', function (Post $post) {
return response()->json($post);
});
Now, when a request is made via /posts/01jkxjdr5r05pfmn3ge4x1kzdq
will fetch the post where ulid = '01jkxjdr5r05pfmn3ge4x1kzdq'
.
- when the id is used on the route it’ll show 404 page:
Explicit Model Binding
Explicit model binding provides full control over how Laravel’s binding logic resolves route parameters to model instances. It allows you to define custom logic for retrieving a model based on any attribute or condition, ensuring flexibility in how data is fetched.
To implement explicit model binding, use the Route::model
method at the beginning og the boot
method of your AppServiceProvider
class:
use App\Models\User;
use Illuminate\Support\Facades\Route;
/**
* Bootstrap any application services.
*/
public function boot(): void
{
Route::model('user_post', Post::class);
}
- Next, define a route that contains a
{user_post}
parameter that can be used in place ofpost
:
use App\Models\Post;
Route::get('/posts/{user_post}', function (Post $post) {
return response()->json($post);
});
Since we have bound all {user_post}
parameters to the App\Models\Post
model, an instance of that class will be injected into the route. So, for example, a request to posts/01jkxjdr5r05pfmn3ge4x1kzdq
will inject the Post
instance from the database which has an ulid
of 01jkxjdr5r05pfmn3ge4x1kzdq
.
- Customizing the Resolution Logic
To customize model binding, use the Route::bind
method in the boot
method of AppServiceProvider
. This closure receives the route parameter and returns the corresponding. For instance, if you prefer to find posts using their ulid
instead of the default id
, you can define it as follows:
use App\Models\Post;
use Illuminate\Support\Facades\Route;
public function boot(): void
{
Route::bind('post', function ($value) {
return Post::where('ulid', $value)->firstOrFail();
});
}
- Instead of the default finding by
id
it’ll search byulid
Route::get('/posts/{post}', function (Post $post) {
return response()->json($post);
});
Now, visiting /posts/
01jkxjdr5r05pfmn3ge4x1kzdq
will fetch the user where ulid = '01jkxjdr5r05pfmn3ge4x1kzdq'
.
Alternatively, You can also override the resolveRouteBinding
method in your Eloquent model to customize how route parameters are resolved into model instances:
/**
* Retrieve the model for a bound value.
*
* @param mixed $value
* @param string|null $field
* @return \Illuminate\Database\Eloquent\Model|null
*/
public function resolveRouteBinding($value, $field = null)
{
return $this->where('name', $value)->firstOrFail();
}
Using Route Model Binding in Controllers
Instead of defining closures in routes, you can pass the bound model directly to a controller.
use App\Http\Controllers\UserController;
use App\Models\Post;
Route::get('/posts/{post}', [PostController::class, 'show']);
- Controller Method
namespace App\Http\Controllers;
use App\Models\Post;
use Illuminate\Http\Request;
class PostController extends Controller
{
public function show(Post $post)
{
return response()->json($post);
}
}
Route Model Binding with Relationships
You can bind models with relationships in nested routes.
Example: Binding a Post That Belongs to a User
use App\Models\User;
use App\Models\Post;
Route::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
return response()->json($post);
});
By default, Laravel does not validate that the
post
belongs to theuser
.To enforce this, use constrained binding.
Enforce Relationship Constraint
phpCopyEditRoute::get('/users/{user}/posts/{post}', function (User $user, Post $post) {
if ($post->user_id !== $user->id) {
abort(404);
}
return response()->json($post);
});
Handling Missing Models Behavior
When a model cannot be found, Laravel throws a ModelNotFoundException
. To handle this gracefully, you can customize the exception rendering in your bootstrap/app.php
file in Laravel 11:
->withExceptions(function (Exceptions $exceptions) {
$exceptions->renderable(function (NotFoundHttpException $e) {
return response()->json([
'message' => $e->getMessage(),
], Response::HTTP_NOT_FOUND);
});
})->create();
Best Practices
Use implicit binding whenever possible to keep your code clean and concise.
Leverage explicit binding when dealing with complex queries or relationships.
Always validate additional parameters using request validation or policies to ensure security.
Ensure missing model behavior error is properly handled.
Conclusion
In this article we’ve learn in-depth about the Laravel Route Model Binding feature that simplifies retrieving Eloquent model instances in routes and controllers. It offers several benefits. It keeps the code cleaner by allowing controllers to focus on business logic rather than data retrieval. It also simplifies error handling, as Laravel automatically returns a 404 response when a model instance isn’t found. Additionally, it improves readability by making it clear from the route definition which model is expected, enhancing code maintainability.
Find this article useful… kindly share with your network and feel free to use the comment section for questions, answers, and contributions.
Follow me on Hashnode: Alemsbaja --- X: Alemsbaja---- Youtube: Tech with Alemsbajato stay updated on more articles