Cache Flag plugin for Craft CMS
Cache Flag is a Craft CMS plugin that adds an alternative cache invalidation strategy to template caches, using manually defined keywords ("flags").
Why does this plugin exist?
Cache Flag was originally designed to circumvent common performance issues with the native {% cache %}
tag's element query based invalidation strategy.
Since Craft 3.5.0, said performance issues have been solved in core, making Cache Flag redundant for its primary use case. If you were previously using Cache Flag only to avoid performance issues with {% cache %}
, you probably don't need it anymore!
However, Cache Flag is still a valid alternative to the native {% cache %}
tag if you want to
- Implement automatic or manual bulk template cache invalidation (optionally, in combination with Craft's native element-based cache invalidation)
- Cache arbitrary HTML output and implement your own invalidation strategies for it
- Have completely cold template caches (like the Cold Cache plugin, which is not available for Craft 3 or 4)
Table of contents
- Requirements and installation
- Using Cache Flag
- Dynamic flags
- Arbitrary flags
- Collecting element tags for automatic cache invalidation
- Cold caches
- Invalidating flagged caches
- Additional parameters
- Project Config
- Upgrading from Craft 2
- Events
Requirements and installation
This plugin requires Craft CMS 3.7.0, 4.0.0-beta.4 or later.
Craft installs using Craft CMS 3.4.x or below should install Cache Flag v. 1.0.4:** composer require mmikkel/cache-flag:1.0.4
To install the plugin, follow these instructions:
-
Open your terminal and go to your Craft project:
cd /path/to/project
-
Then tell Composer to load the plugin:
composer require mmikkel/cache-flag
-
In the Control Panel, go to Settings → Plugins and click the “Install” button for Cache Flag, or install via the Craft CLI:
./craft plugin/install cache-flag
Using Cache Flag
Cache Flag adds a new {% cacheflag %}
Twig tag to Craft CMS, which works just like the native {% cache %}
tag - except that by default, Cache Flag's template caches are "cold" (i.e. Cache Flag will not save element queries for automatic cache invalidation).
For cache invalidation, Cache Flag adds the ability to "flag" template caches and content with keywords ("flags"). Whenever an element is saved, moved or deleted, Cache Flag will automatically invalidate any flagged template caches matching that element's flags.
Here's how it looks in action:
{% cacheflag flagged "news|images" %}
{% set entries = craft.entries.section('news').all() %}
...
{% endcacheflag %}
Note that multiple flags are separated using the pipe delimiter (|
).
Tip: In addition to the flagged
parameter it's also possible to have Cache Flag clear caches automatically in the same way the native {% cache %}
tag does, using the new with elements
directive.
I'm going to need an example.
Sure. Let's assume you have a section called "News", and there's a cache that you want to invalidate whenever the content in that section changes (i.e. if entries are saved, deleted, changes status etc). First, you add the flag news
(or whatever, the flags can be anything, really) to the "News" section in Cache Flag's CP section:
Then, you add that same news
flag to any relevant caches, using the {% cacheflag %}
tag and the flagged
parameter:
{% cacheflag flagged "news" %}
{% set entries = craft.entries... %}
...
{% endcacheflag %}
Now, whenever an entry in the "News" section is saved, moved, deleted or changes status, any caches flagged with news
will be automatically invalidated.
Dynamic flags
It's possible to flag caches using dynamic flags based on element IDs and/or UIDs. If you wanted to ensure that a cache is invalidated whenever a particular element is edited, moved or deleted, you can do this:
{% cacheflag flagged "entry:#{entry.id}" %}
...
{% endcacheflag %}
or if you prefer:
{% cacheflag flagged "entry:#{entry.uid}" %}
...
{% endcacheflag %}
All native element types can be used in dynamic flags:
entry:#{entry.id}
asset:#{asset.id}
category:#{category.id}
tag:#{tag.id}
globalSet:#{globalSet.id}
user:#{user.id}
It's also possible to use the element
prefix, which works for all element types (including custom/third party ones):
element:#{element.id}
element:#{element.uid}
Of course, it's possible to combine both standard and dynamic cache flags for a single cache:
{% cacheflag flagged "news|employees|entry:#{entry.id}|category:#{category.id}" %}
...
{% endcacheflag %}
Arbitrary flags
The flags you add to your {% cachetags %}
caches can be literally anything - and they don't have to be added to an element source (or be dynamic).
A good use case for arbitrary flags is when you've got a cache that don't involve any elements, for example if you wanted to cache output dependent on an external API call or something else that is time-consuming to parse on every request, e.g. something like this:
{% cacheflag flagged "somearbitraryflag" %}
{% set data = craft.somePlugin.doExpensiveApiCall() %}
...
{% endcacheflag %}
If you use arbitrary flags, keep in mind that there's nothing that will actually invalidate those caches automatically (they'll essentially be cold caches, albeit flagged). Read up on the different options available for invalidating these - and other - flagged caches here.
Collecting element tags for automatic cache invalidation
Since Cache Flag 1.1.0 (Craft 3.5.0-RC1 or later), it's possible to collect element tags (in addition to your own flags) for automatic cache invalidation just like the native {% cache %}
tag does.
If you want Cache Flag to collect element tags for automatic cache invalidation, you can add the with elements
directive like this:
{% cacheflag flagged "awesome" with elements %}
...
{% endcacheflag %}
Note: It's also possible to omit the flagged
parameter and only use with elements
, but at that point the {% cacheflag %}
tag would work identically to the native {% cache %}
tag, and you should probably just use the latter.
Cold caches
If both flagged
and with elements
are omitted from a {% cacheflag %}
tag, that cache will be completely "cold", and it will only be invalidated if/when it expires, or if a user manually invalidates it (or clears the entire data cache) via the Control Panel or the Craft CLI (see also invalidating flagged caches):
{% cacheflag for 360 days %}
...
{% endcacheflag %}
Tip: If you're upgrading a Craft 2 site that uses the Cold Cache plugin, this is one way to achive the same thing on Craft 3.
Invalidating flagged caches
Cache Flag will automatically invalidate any caches with flags saved to one or multiple element sources in Cache Flag's CP section, and caches using dynamic flags. These caches are invalidated whenever relevant elements are saved, deleted, moved or changes status.
Cold caches and caches using arbitrary flags must be invalidated manually or programmatically (see below).
Manual cache invalidation
Flagged template caches can be manually invalidated by
- Using the native Clear Caches utility in the Craft CP (check out the CP Clear Cache plugin for easier access to this tool)
- Hitting the "Invalidate all flagged caches" button in Cache Flag's CP section (only available if
allowAdminChanges
istrue
, see more info here) - Using the native CLI command
invalidate-tags/cacheflag
- Using the native CLI command
invalidate-tags/template
(invalidates all template caches, including flagged ones) - Using the native CLI command
invalidate-tags/all
(invalidates all caches, including template caches)
Additionally, Cache Flag exposes its own cache-flag/caches/invalidate
CLI command, that can be used if you want to clear flagged template caches for specific flags (this also works with arbitrary flags):
./craft cache-flag/caches/invalidate news,images,awesome
If you want to clear flagged caches over HTTP there's also a web controller action cache-flag/caches/invalidate
which can be hit with a GET or POST request. This controller action will invalidate all flagged template caches, unless a parameter flags
(string[]; array of flags) is present in the request.
Finally, flushing the data cache will delete all template caches, including flagged ones.
Programmatic cache invalidation
use mmikkel\cacheflag\CacheFlag;
// Invalidate all flagged caches
CacheFlag::getInstance()->cacheFlag->invalidateAllFlaggedCaches();
// Invalidate caches for a particular element
CacheFlag::getInstance()->cacheFlag->invalidateFlaggedCachesByElement($entry);
// Invalidate caches for one or several flags
CacheFlag::getInstance()->cacheFlag->invalidateFlaggedCachesByFlags(['news', 'images']);
Additional parameters
Beyond the flagged
and with elements
parameters, the {% cacheflag %}
tag supports all the same parameters as [the native {% cache %}
tag[(https://docs.craftcms.com/v3/dev/tags/cache.html#app)].
allowAdminChanges
Project Config and Cache Flag supports Project Config since v. 1.2.0 (Craft 3.5.0 or later only). If you're upgrading from an earlier version of Cache Flag, the relevant .yaml
files will be automatically created after upgrading and running migrations.
Note that Cache Flag's CP section is inaccessible in environments where the allowAdminChanges
config setting is set to false
.
Upgrading from Craft 2
Cache Flag will attempt to automatically migrate flags from the old templatecaches_flagged
Craft 2 database table after installation (or, after upgrading from an earlier version of Cache Flag for Craft 3).**
The migration only runs if
- The old Craft 2 database table
templatecaches_flagged
is still in the database - The new Craft 3 database table (
cacheflag_flags
) has no rows
After the migration has completed, make sure that all flags have carried over. Any missing flags will have to be manually entered in Cache Flag's CP section (this can happen if there isn't parity between element source IDs in the Craft 2/3 database tables).
Events
Cache Flag dispatches two events:
-
beforeInvalidateFlaggedCaches
Dispatched just before Cache Flag invalidates one or several flagged template caches. -
afterInvalidateFlaggedCaches
Dispatched immediately after Cache Flag has invalidated one or several flagged template caches.
Both events include a parameter flags
, which is an array of the flags Cache Flag is invalidating caches for.
Listening to Cache Flag events
use mmikkel\cacheflag\events\FlaggedTemplateCachesEvent;
use mmikkel\cacheflag\services\CacheFlagService;
use yii\base\Event;
Event::on(
CacheFlagService::class,
CacheFlagService::EVENT_BEFORE_INVALIDATE_FLAGGED_CACHES,
function (FlaggedTemplateCachesEvent $event) {
$flags = $event->flags;
...
}
);
Event::on(
CacheFlagService::class,
CacheFlagService::EVENT_AFTER_INVALIDATE_FLAGGED_CACHES,
function (FlaggedTemplateCachesEvent $event) {
$flags = $event->flags;
...
}
);
Note: Before Cache Flag 1.1.0, the EVENT_AFTER_DELETE_FLAGGED_CACHES
(now deprecated in favor of EVENT_AFTER_INVALIDATE_FLAGGED_CACHES
) would only be dispatched if caches were actually deleted. In Cache Flag 1.1.0+, the EVENT_AFTER_INVALIDATE_FLAGGED_CACHES
event is dispatched regardless of whether any caches were actually cleared.