Follow on twitter
Apr 24th, 2019

Speed up your Laravel pages by caching the responses

Every time you access an URL on a Laravel application, many things happen under the hood to process the given request.

If you take a quick look at the kernel.php file, you’ll see some of the “tasks” that are going to be processed for each request.

<?php
protected $middleware = [
\App\Http\Middleware\CheckForMaintenanceMode::class,
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
\App\Http\Middleware\TrimStrings::class,
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
\App\Http\Middleware\TrustProxies::class,
\Nckg\Minify\Middleware\MinifyResponse::class
];
/**
* The application's route middleware groups.
*
* @var array
*/
protected $middlewareGroups = [
'web' => [
\App\Http\Middleware\EncryptCookies::class,
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
\Illuminate\Session\Middleware\StartSession::class,
// \Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\App\Http\Middleware\VerifyCsrfToken::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
],
view raw 1.php hosted with ❤ by GitHub

This is just the “middleware” layer, and depending on the complexity of your application, you may have a lot of database queries, third-party API calls and so on.

Read more about the Laravel Lifecycle here: Request Lifecycle - Laravel - The PHP Framework For Web Artisans

Sometimes, there is no easy way to go around and “recycle” part of the processed data for further requests, but in Laravel, you can use “Cache” to store some data temporarily.

So, for instance, in your controller you can have something like:

<?php
public function show($id)
{
$post = Cache::remember("post-{$id}", function () use ($id) {
return Post::find($id);
}
return view('post')->withPost($post);
}
view raw 2.php hosted with ❤ by GitHub

The next time you request the same Post::class Laravel will return the data from the cache storage instead of hitting the database again.

This is just fine in most cases, but you can go even further.

Caching an entire response

The spatie/laravel-responsecache is a package developed by the spatie.be team that allows you to store an entire response-object in cache, so, if a given URL was already crawled, the next user will get a faster response because the application won’t have to run through the entire lifecycle all over again.

To install this package in your Laravel application just run the following command from your terminal:

composer require spatie/laravel-responsecache

And register the middlewares in your kernel.php file:

<?php
protected $routeMiddleware = [
...
'doNotCacheResponse' => \Spatie\ResponseCache\Middlewares\DoNotCacheResponse::class,
'cacheResponse' => \Spatie\ResponseCache\Middlewares\CacheResponse::class,
];
view raw 3.php hosted with ❤ by GitHub

To learn more about this package, you can read the official documentation here: GitHub - spatie/laravel-responsecache: Speed up a Laravel app by caching the entire response

My current Implementation

At the moment of this writing, I’m using this package on my website using the base setup described above, and registering all the routes that I want to cache under the same group as follows:

<?php
// Out of cache
Route::get('/preview/{slug}', 'ArticleController');
/**
* Cached routes
*/
Route::middleware('cacheResponse')->group(function () {
Route::get('/{slug}', 'ArticleController');
});
view raw 4.php hosted with ❤ by GitHub

What about content updates?

By default, the package will store a response for a week, so that would be a problem if you update the content for a cached URL and you don’t handle that properly.

I solved this problem by creating a new PostObserver to listen for the saved() and updated() methods to remove the specific article from cache every time one of those events are fired.

<?php
namespace App\Observers;
use App\Post;
use Spatie\ResponseCache\Facades\ResponseCache;
class PostObserver
public function saved(Post $post)
{
ResponseCache::forget(url($post->slug));
}
public function saved(Post $post)
{
ResponseCache::forget(url($post->slug));
}
```
view raw 5.php hosted with ❤ by GitHub

Remember you’ll need to register the observers in your AppServiceProvider::class:

<?php
namespace App\Providers;
use App\Post;
use App\Observers\PostObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
Post::observe(PostObserver::class);
}
view raw 6.php hosted with ❤ by GitHub

So, now every time an article is updated, the observer will take care of removing that URL from the cache.

Wrapping up

Using cache in the responses is a great approach to speed up your application, although you may want to be careful with it, primarily when you use real-time data or some user-specific information.

Jeff
6 months ago
Share: