Programatically invalidating specific entries from Statamic's static cache
Static caching is one of my favourite features in Statamic - and one that can insanely boost your site’s performance.
Coming in two flavours, half-measure and full-measure, full is the performance magic: when a page is requested, Statamic will put a full HTML render of the page on your server, and your server’s rewrite rules will then serve that on future requests.
And don’t worry, you can use a nocache tag to keep parts of your site dynamic too. But static caching offloads the most expensive parts of your site - rendering navs, looping through page builder blocks, doing all of the heavy lifting - and renders them out for performance.
Your site’s static caching configuration also has incredible rules about what you want to do when an Entry from a specific collection is invalidated: if you have a Blog, like this site, when I publish a new Entry, I want the page itself to be invalidated, but also the index pages for the Blog plus tag-related index pages. And Statamic does all of this for you out of the box.
And during deployment, there’s a command you can include in your deployment script that flushes your site’s entire cache.
But what about when you don’t want the entire cache flushed? Or need to programatically clear specific entries?
Recently on Discord, Aaron Bushnell has shown that there are valid use cases where you want to clear specific pages from your site’s static cache. The Scheduled Cache Invalidator addon I wrote tackles this problem for scheduled content - but the same ideas can be used for programmatic clearing too, such as a scheduled command in Aaron’s case.
If you’re using full measure and need to clear a file, sure you could manually remove the rendered HTML from the server (so on the next request, the actual Statamic instance renders the page)… but what about all of additional rules.
This is where Statamic’s Invalidator comes in.
Using Laravel’s service container, it is trivial to get Statamic’s invalidator - this is the powerful helper that can do the hard work for us.
1use Statamic\StaticCaching\Invalidator;2 3$invalidator = app(Invalidator::class);
This has a single method - invalidate - and expects an item. It could be an Entry, or an Asset, or any of the other content items within the Statamic ecosystem. It’ll know what to do next.
Following on from Aaron’s use case, let’s say there is a specific Entry you want to clear - we just need to pass it to the invalidate method:
1use Statamic\Facades\Entry;2use Statamic\StaticCaching\Invalidator;3 4$invalidator->invalidate(/* your Entry, Asset, Term... */);
Maybe you have a number of pages you need to clear - you could loop through them:
1use Statamic\Facades\Entry; 2use Statamic\StaticCaching\Invalidator; 3 4$urls = [ 5 '/my-url', 6 '/clear-this-too', 7 '/blog/this-is-a-different-collection-too', 8]; 9 10$invalidator = app(Invalidator::class);11 12foreach ($urls as $url) {13 $entry = Entry::findByUri($url);14 $invalidator->invalidate($entry);15}
With this simple loop, you’re clearing three Entries from the static cache based on their URL.
But why is it so important to use the Invalidator?
Well this is where Statamic’s smarts come in to play.
If you’ve configured rules, like for the Blog example above, and you invalidate a Blog Entry, Statamic will also know to follow the rules you’ve defined, clearing any of the related static cache entries too.
And if you have Entries that live in different Collections, each Collection’s invalidation rules will be used too.
This means that with a single invalidate call - programatically - you are able to hook in to Statamic’s static caching. No fuss, no worrying about rules: Statamic does that heavy work for you.
Want to make it a command? Easy!
1<?php 2 3namespace App\Console\Commands; 4 5use Illuminate\Console\Command; 6use Statamic\Facades\Entry; 7use Statamic\StaticCaching\Invalidator; 8 9class ClearSelectedCachedPagesCommand extends Command10{11 protected $signature = 'pages:clear-selected';12 13 protected $description = 'Removes three specific URLs from the Statamic static cache.';14 15 public function handle(): void16 {17 $urls = [18 '/my-url',19 '/clear-this-too',20 '/blog/this-is-a-different-collection-too',21 ];22 23 $invalidator = app(Invalidator::class);24 25 foreach ($urls as $url) {26 $entry = Entry::findByUri($url);27 $invalidator->invalidate($entry);28 }29 }30}
And with that, especially if you don’t like closures in your console.php file, you can now run this command on a schedule - maybe hourly, daily, minutely… but you can have the benefits of static caching, and the piece of mind of ensuring your time-sensitive pages remain automatically refreshing.