Caching Laravel routes with Statamic's static cache

Published July 15th, 2024

In case you missed it in my recent post about clearing Statamic’s static cache from an external Laravel app, I thought it would be an idea to extract this little nugget.

Did you know: you can write Laravel routes in your Statamic site, and have the output of these routes cached with Statamic’s static cache?

Seriously cool.

Out of the box, Statamic creates URLs for each of your site’s mounted Entries - and this happens automatically. It also means an author in Statamic’s CP can edit that Entry, and see the changes reflected at the Entry’s URL. You know, like any CMS would do.

There are also situations where you need to create your own Laravel route to give your site a URL that is not part of Statamic’s Entries setup. Remember, Statamic is built on top of Laravel so you have all of Laravel’s features at your disposal: including routing and controllers.

For a recent site, I created a number of Laravel routes that needed to pull data from an external API to help populate stats from an external app on a whole subsection of a site.

They’re still pages in the site: they need a URL that can be linked to, shared and accessed; they need to look like the rest of the site. And they also need to be statically cached to help with performance given the necessity of the API calls. But they’re not Entries within Statamic - there’s no content for an author to edit directly within the CP. They’re just a Laravel route with a controller building the page.

To get this up and running we need three things:

  1. A route definition, and a Controller (or as a closure if you wanted - but I like using a Controller)

  2. A Statamic View response, and

  3. Statamic’s static caching Cache middleware

The route and Controller

If you’re already building Laravel apps, this will be a no-brainer. You can define routes within your Statamic site in Laravel’s routes/web.php file, just like you would in a standard Laravel app:

1use App\Http\Controllers\MyRouteController;
2 
3Route::get('/my-route', [MyRouteController::class, 'show'])
4 ->name('my-route');
Copied!

We’ve got a GET route (that the visitor can view in a browser), and are simply calling the show method in our Controller, and giving it a name. The name is optional: I just find it useful to name routes for when I need to use them in other places. So far nothing too tricky here.

And, if you have Models in your app that relate to your route, you can also use Laravel’s route model binding. If you don’t have Models, you can still use route parameters too.

The Statamic View

Within our Controller, we can do whatever we need to do. Load a local file and process its contents, call an API, clear a cache, write custom queries with Statamic’s Query Builders, whatever you need.

The key part is that your Controller needs to return a new \Statamic\View\View instance.

1<?php
2 
3namespace App\Http\Controllers;
4 
5class NationalController extends Controller
6{
7 public function show()
8 {
9 // work your magic here... call an API, clear a cache, do a little dance
10 
11 // then return a Statamic View instance
12 return (new \Statamic\View\View);
13 }
14}
Copied!

There are three key things we need to do with our View instance.

Firstly, we can call its template method to pass across the specific template you want to use.

1return (new \Statamic\View\View)
2 ->template('partials/my-route');
Copied!

This template can be an Antlers or Blade template file, and Statamic will pass your View’s data across to it to render out some HTML markup. In the above, let’s say it is Antlers, we’d have a file resources/views/partials/my-route.antlers.html in our site. How you want to organise your site’s templates is up to you: just be smart and find a way that is logical for your team.

This is great way to write a smaller section of your page that will be populated in the template_content variable within your layout. Ah yes, the layout.

Secondly, we can call the layout method to pass the overall layout you want to use.

1return (new \Statamic\View\View)
2 ->template('partials/my-route')
3 ->layout('layout');
Copied!

The layout is the overall container layout for the page, and would include things like the head and body tags, and pull in your site’s styles and scripts. Most likely this will also be the layout you use for your site’s Entry-based pages.

If you’re re-using your site’s layout, you’ll already have the template_content variable in use: Statamic needs this for your Entry-based layouts. If it is not in play, you’ll need to include this variable somewhere in your layout - this is the variable where Statamic will store the rendered markup from the processing of your template.

And finally, you can call the with method to pass an associative array of data along to the template:

1return (new \Statamic\View\View)
2 ->template('partials/my-route')
3 ->layout('layout')
4 ->with([
5 'foo' => 'bar',
6 'mickey' => 'mouse',
7 ]);
Copied!

In this example, within our partials/my-route.antlers.html file, we’ll have access to two variables - foo and mickey. We can use those as we would any other variable within our template. And of course within our template we can also use Statamic’s tags and helpers (for Globals and modifiers) as we need too. We’re still working with Statamic, after all.

This is a really simple and trivial example - but your with call could be rather chunky too. If you’ve retrieved data from an API, it could be a lengthy array; if you’ve done some heavy lifting within your own site, it could be processed data or structures relevant to the layout of the page. Even nested arrays. You’ll get access to it all within your template.

The important part: pass a key/value array of whatever variables you’ll need in your template.

If you visit your route in a browser, your controller code will run, and Statamic will render your View’s template, place it in your View’s layout, and use the data array you’ve passed along too. This is a great start - but if you’re having a computationally expensive call - like external APIs can be - there’s one bit we need to add to let Statamic also include it in your static cache.

The Cache middleware

This is super easy - all you need to do is jump back to your route definition in your routes/web.php file, and add Statamic’s Cache middleware to your route:

1use App\Http\Controllers\MyRouteController;
2use Statamic\StaticCaching\Middleware\Cache;
3 
4Route::get('/my-route', [MyRouteController::class, 'show'])
5 ->middleware([Cache::class])
6 ->name('my-route');
Copied!

And that’s it. As long as you have Statamic’s static caching configured correctly, that’s all you need to do. This middleware is responsible for hooking up your configured static cacher, determining if the route should be cached, and if so, handling and finalising the request - so under the hood it is being handled the same way that viewing an Entry would be too.

If your site has a number of routes, you can also use Laravel’s grouping to apply the middleware to the group too:

1use App\Http\Controllers\AnotherRouteController;
2use App\Http\Controllers\MyRouteController;
3use Statamic\StaticCaching\Middleware\Cache;
4 
5Route::group(['middleware' => [Cache::class]], function () {
6 
7 Route::get('/my-route', [MyRouteController::class, 'show'])
8 ->name('my-route');
9 
10 Route::get('/another-route', [AnotherRouteController::class, 'show'])
11 ->name('another-route');
12 
13});
Copied!

How awesome is this though:

  • A Laravel route for your site,

  • that uses your familiar Antlers (or Blade) templating,

  • that hooks in to Statamic’s static caching,

  • can re-use your site’s layout, and

  • takes advantage of your site’s setup and configuration (and everything else Statamic and Laravel have to offer).

This is what makes Statamic truly awesome and open-ended: the possibilities with this are endless.

I love using Statamic’s full static caching to really make a website fly. After all, most websites are static content. And of course, for those times where your need dynamic elements in your page, Statamic has a number of ways to keep it fresh - from a nocache tag to half measure caching to scheduled cache clearing to advanced clearing methods: it depends on your use case, but because Statamic is built on Laravel, you’ve got a suite of methods at your disposal.

You may be interested in...