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:
A route definition, and a Controller (or as a closure if you wanted - but I like using a Controller)
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');
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 dance10 11 // then return a Statamic View instance12 return (new \Statamic\View\View);13 }14}
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');
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');
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 ]);
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');
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});
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.