/ Laravel

Cached models and route model bindings in Laravel

If you're familiar with Implicit Route model binding in Laravel (and you should be), you know it's the best thing ever. No more $post = Post::where('key', $key)->firstOrFail(); in your controllers. However, implicit binding ALWAYS performs a database query to fetch the record by the route key:

public function resolveRouteBinding($value)
{
    return $this->where($this->getRouteKeyName(), $value)->first();
}

What if we don't want that? What if we want to cache, for example post, and try to get it from the cache first and fallback to the database query.
Instead of Route::get('posts/{post}', function (Post $post) { ... }), what if we could call something like Route::get('posts/{cachedPost}', function (Post $post) { ... }).
I came up with a nifty trick that allows me to do that: ceate a custom binding in your RouteServiceProvider@boot.
In the following snippet, $cacheKey is your cache key generated for the post (resolved from the config?), and the $cacheTime is cache expiration for the resource. Note that $post variable in the closure is actually value for the route key. For example, if you go to /posts/my-awesome-post, $post variable will be my-awesome-post. On line 11 we resolve the Post class from the Container and try to resolve the model for a route binding. The model is returned if found, otherwise, we throw the same exception that Laravel throws if it cannot resolve the model by the route key. All of that is wrapped in the Cache::remember(), and that function will fail if Exception will thrown and nothing will be cached.

// app/Providers/RouteServiceProvider.php
public function boot()
{
    parent::boot();

    $this->bind('cachedPost', function ($post) {
        $cacheKey = 'posts.' . $post; // posts.my-awesome-post
        $cacheTime = now()->addDay();

        return $this->app['cache']->remember($cacheKey, $cacheTime, function () use ($post) {
            if ($model = $this->app->make(Post::class)->resolveRouteBinding($post)) {
                return $model;
            }

            throw (new ModelNotFoundException)->setModel(Post::class);
        });
    });
}

Now, all you gotta do is use it as {cachedPost} in your routes and you're all set.


The following snippet was inspired by Laravel's own route model binding and can be reused for any resource you want to cache.

Cached models and route model bindings in Laravel
Share this

Subscribe to Josip Crnković