Implicit route model binding in Laravel 5.2

Posted on December 18, 2015 | By Matt Stauffer


Warning: This post is over a year old. I don't always update old posts with new information, so some of this information may be out of date.

If you've never used it, Laravel's route model binding has been around for a while, but Laravel 5.2 is about to make it even easier.

The basics of route model binding

Let's assume that a common pattern for binding a URL route is something like this:

Route::get('shoes/{id}', function ($id) {
    $shoe = Shoe::findOrFail($id);
    // Do stuff
});

This is something I do a lot. Wouldn't it be nice if you could drop the findOrFail line and just teach Laravel's router that this route represents a Shoe? You can. In your route service provider, just teach the router: $router->model('shoe', 'App\Shoe'); That means, "any time I have a route parameter named shoe, it's an ID representing an instance of App\Shoe". This allows us to re-write the above code like this:

Route::get('shoes/{shoe}', function ($shoe) {
    // Do stuff
});

Implicit route model binding

In Laravel 5.2, it's even easier to use route model binding. Just typehint a parameter in the route Closure (or your controller method) and name the parameter the same thing as the route parameter, and it'll automatically treat it as a route model binding:

Route::get('shoes/{shoe}', function (App\Shoe $shoe) {
    // Do stuff
});

That means you can now get the benefits of route model binding without having to define anything in the Route Service Provider. Easy!

That's it for implicit route model binding! Everything past this point is already around in 5.1.

Little known features of route model binding

These features are not new with 5.2, and therefore not specific to implicit route model binding, but they seem to be not commonly known, so I thought I would throw them in here.

Custom binding logic for route model binding

If you want to customize the logic a route model binding uses to look up and return an instance of your model, you can pass a Closure as the second parameter of an explicit bind instead of passing a class name:

$router->bind('shoe', function ($value) {
    return App\Shoe::where('slug', $value)->where('status', 'public')->first();
});

Custom exceptions for route model binding

You can also customize the exceptions that the route model bindings throw (if they can't find an instance of that model) by passing a Closure as the third parameter:

$router->model('user', 'App\User', function () {
    throw new NotFoundHttpException;
});

Changing an Eloquent model's "route key"

By default, Laravel assumes an Eloquent model should map to URL segments using its id column. But what if you expect it to always map to a slug, like in my shoe custom binding logic example above?

Eloquent implements the Illuminate\Contracts\Routing\UrlRoutable contract, which means every Eloquent object has a getRouteKeyName() method on it that defines which column should be used to look it up from a URL. By default this is set to id, but you can override that on any Eloquent model:

class Shoe extends Model 
{
    public function getRouteKeyName()
    {
        return 'slug';
    }
}

Now, I can use explicit or implicit route model binding, and it will look up shoes where the slug column is equal to my URL segment. Beautiful.


Comments? I'm @stauffermatt on Twitter


Tags: laravel  •  laravel 5.2