Caching is a great way to speed up an application. You can cache views, queries, variables, and tons of other things very easily, but what if you want to cache an entire response?
There are a few different places we could cache a response, in the controller is where I see it done most of the time, but I wanted a more general-use method of caching a response. A great way to do it is in a middleware class.
Our middleware needs to do two things:
- Check if our response has been cached, and if it has then return it
- If it has not been cached, cache it for the next request
For the first requirement, I’ll use the standard handle() method you see in most middleware
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$key = $this->generateKey($request->fullUrl());
if (Cache::has($key)) {
// you can also run some cache invalidating logic here if needed
return response(Cache::get($key));
}
return $next($request);
}
We need some sort of unique key for our cached response, so I decided to take the full URL and run it through a simple function to generate a slugged string of the incoming URL
protected function generateKey($url)
{
return 'response_' . str_slug($url);
}
For the second requirement we can use terminate() . From the docs
If you define a terminate method on your middleware, it will automatically be called after the response is sent to the browser.
So after a response has been sent to the browser, we’ll get that response back and then we can cache it.
/**
* Handle caching of a request if it doesn't already exist
*/
public function terminate($request, $response)
{
$key = $this->generateKey($request->fullUrl());
// if the cache key doesn't exist then we store the entire response
if (!Cache::has($key)) {
// cache for 24 hours
Cache::put($key, $response->getContent(), 1440);
}
}
Here I’m storing the response for 24 hours, but you can raise or lower that depending on your needs.
Full middleware
<?php
namespace App\Http\Middleware;
use Closure;
use Cache;
class CacheResponse
{
/**
* Handle an incoming request.
*
* @param \Illuminate\Http\Request $request
* @param \Closure $next
* @return mixed
*/
public function handle($request, Closure $next)
{
$key = $this->generateKey($request->fullUrl());
if (Cache::has($key)) {
return response(Cache::get($key));
}
return $next($request);
}
/**
* Handle caching of a request if it doesn't already exist
*/
public function terminate($request, $response)
{
$key = $this->generateKey($request->fullUrl());
// if the cache key doesn't exist then we store the whole response
if (!Cache::has($key)) {
// cache for 24 hours
Cache::put($key, $response->getContent(), 1440);
}
}
protected function generateKey($url)
{
return 'response_' . str_slug($url);
}
}
Now we need to register our middleware in app/Http/Kernel.php which you can do by adding our new class to the $routeMiddleware array
'cacheroute' => \App\Http\Middleware\CacheResponse::class,
And finally, add the middleware to any routes that you want to cache
Route::get('/', function () {
return view('welcome');
})->middleware('cacheroute');
And now you have simple middleware that you can use to cache any of your responses.