Cache Management: Hook-Driven Cache System for Developers
PolyCMS ships with a centralized Cache Management system that provides administrators with a UI to inspect and clear caches — and gives developers a powerful hook-driven API to integrate their own caching logic without conflicting with core or other modules.
This guide covers the architecture, available hooks, and practical examples for module and theme developers who want to extend, replace, or react to cache operations.
Architecture Overview
The CacheService (app/Services/CacheService.php) is the single entry point for all cache operations. It maintains a registry of cache types grouped into four categories:
| Group | Description | Built-in Types |
|---|---|---|
laravel |
Laravel framework caches | Application, View, Config, Route, Event |
polycms |
PolyCMS internal service caches | Theme, Module, Settings, Template Resolver |
server |
Server-level caches | PHP OPcache |
module |
Module-registered caches | (Your module adds here) |
The system exposes 7 hooks and filters that modules can use to participate in cache management.
Hook & Filter Reference
Filters (Value Transformers)
These hooks allow modules to modify data before it is used.
cache.types — Register Custom Cache Types
Add your own cache types to the system. Your type will appear in the admin Cache Management page alongside core types.
Signature: array $types
use App\Facades\Hook;
Hook::addFilter('cache.types', function (array $types) {
$types[] = [
'key' => 'cdn_purge',
'label' => 'CDN Cache',
'description' => 'Purge Cloudflare or Fastly edge cache',
'group' => 'module', // Use 'module' for custom types
'clearable' => true,
];
return $types;
});
Required fields per type:
| Field | Type | Description |
|---|---|---|
key |
string |
Unique slug (used in API calls) |
label |
string |
Human-readable name |
description |
string |
Short explanation shown in admin UI |
group |
string |
laravel, polycms, server, or module |
clearable |
bool |
Whether the "Clear" button should appear |
cache.type.handler — Claim Ownership of a Cache Type
This is the conflict prevention mechanism. When a module claims a cache type, the core will skip its own clearing logic for that type and delegate entirely to the module's cache.clear.{type} action hook.
Signature: ?string $handler, string $type
Use case: You are building a dedicated Redis management module and want to handle the application cache differently than the default php artisan cache:clear.
Hook::addFilter('cache.type.handler', function (?string $handler, string $type) {
if ($type === 'application') {
return 'RedisManager'; // Your module name — core skips its clear logic
}
return $handler; // Return null for types you don't handle
}, 10, 2);
When a handler is set, the admin UI displays a badge showing which module manages that cache type.
cache.status — Enrich Status Data
Add custom metrics or diagnostics to the cache status response.
Signature: array $status
Hook::addFilter('cache.status', function (array $status) {
// Add Redis memory info to the status payload
$status['redis_info'] = [
'used_memory_mb' => Redis::info()['used_memory_human'] ?? 'N/A',
'connected_clients' => Redis::info()['connected_clients'] ?? 0,
];
return $status;
});
Actions (Event Callbacks)
These hooks fire at specific lifecycle points. They do not return values.
cache.clearing — Before a Type is Cleared
Fires immediately before any cache type is cleared. Use for logging or pre-clear snapshots.
Signature: string $type
Hook::addAction('cache.clearing', function (string $type) {
Log::info("Cache clear starting: {$type}");
});
cache.clear.{type} — Handle Clearing for a Specific Type
This is the primary hook for implementing your own clearing logic. If your module has claimed a type via cache.type.handler, this is where the actual work happens.
Signature: void
// Handle clearing for your custom CDN cache type
Hook::addAction('cache.clear.cdn_purge', function () {
CloudflareApi::purgeEverything(config('services.cloudflare.zone_id'));
});
// Or override the core's application cache clearing
Hook::addAction('cache.clear.application', function () {
Redis::connection('cache')->flushdb();
});
cache.cleared — After a Type is Cleared
Fires after a cache type has been cleared (or attempted), with a success flag.
Signature: string $type, bool $success
Hook::addAction('cache.cleared', function (string $type, bool $success) {
if ($success) {
Log::info("Cache cleared successfully: {$type}");
} else {
Log::warning("Cache clear failed: {$type}");
}
}, 10, 2);
cache.clear_all.before / cache.clear_all.after
Fires before and after a bulk clear operation (when the admin clicks "Clear All Caches").
Hook::addAction('cache.clear_all.before', function (array $types) {
// $types = ['application', 'view', 'config', 'route', 'event', ...]
Log::info('Bulk cache clear starting for: ' . implode(', ', $types));
});
Hook::addAction('cache.clear_all.after', function (array $results) {
// $results = ['application' => 'success', 'view' => 'success', ...]
$failed = array_filter($results, fn($r) => $r !== 'success');
if (empty($failed)) {
Log::info('All caches cleared successfully');
}
});
REST API Endpoints
The Cache Management system exposes two authenticated API endpoints:
GET /api/v1/system/cache/status
Returns the current status of all registered cache types.
Response:
{
"data": {
"driver": "file",
"store": "file",
"types": [
{
"key": "application",
"label": "Application Cache",
"description": "General key-value cache store",
"group": "laravel",
"clearable": true,
"handler": null,
"info": { "driver": "file" }
},
{
"key": "view",
"label": "View Cache",
"group": "laravel",
"clearable": true,
"handler": null,
"info": { "compiled_count": 75 }
}
]
}
}
POST /api/v1/system/cache/clear
Clear one or more cache types.
Request body:
{
"types": ["view", "config", "theme"]
}
Or clear everything:
{
"types": ["all"]
}
Response:
{
"success": true,
"results": {
"view": "success",
"config": "success",
"theme": "success"
},
"message": "All selected caches cleared successfully."
}
Practical Examples
Example 1: CDN Purge Module
A complete module that registers a CDN cache type and handles its clearing:
// In your module's ServiceProvider boot() method
use App\Facades\Hook;
public function boot(): void
{
// Register the cache type
Hook::addFilter('cache.types', function (array $types) {
$types[] = [
'key' => 'cdn',
'label' => 'CDN Edge Cache',
'description' => 'Purge Cloudflare edge cache for all zones',
'group' => 'module',
'clearable' => true,
];
return $types;
});
// Handle clearing
Hook::addAction('cache.clear.cdn', function () {
$zoneId = config('services.cloudflare.zone_id');
$apiToken = config('services.cloudflare.api_token');
Http::withToken($apiToken)
->post("https://api.cloudflare.com/client/v4/zones/{$zoneId}/purge_cache", [
'purge_everything' => true,
]);
});
}
Example 2: Full-Page Cache Module (Taking Over Core)
A module that replaces the default application cache handler with a Varnish-based solution:
public function boot(): void
{
// Claim the 'application' cache type
Hook::addFilter('cache.type.handler', function (?string $handler, string $type) {
if ($type === 'application') {
return 'VarnishCache';
}
return $handler;
}, 10, 2);
// Provide custom clearing logic
Hook::addAction('cache.clear.application', function () {
// Ban all objects from Varnish
Http::withHeaders(['X-Purge' => '.*'])
->request('BAN', config('services.varnish.host'));
// Also clear Laravel's cache store
Artisan::call('cache:clear');
});
}
Example 3: Theme Functions — Auto-Clear on Setting Save
A theme that clears its own compiled assets when Theme Options are saved:
// In themes/my-theme/functions.php
Hook::addAction('cache.cleared', function (string $type, bool $success) {
if ($type === 'theme' && $success) {
// Regenerate compiled theme CSS
$css = MyTheme::compileStyles();
file_put_contents(public_path('themes/my-theme/compiled.css'), $css);
}
}, 10, 2);
Auto-Clear on Theme Upload
When a theme is uploaded or updated via the admin panel, PolyCMS automatically clears the following caches:
- View Cache — Removes stale compiled Blade templates
- Config Cache — Ensures new
functions.phpsettings definitions are loaded - OPcache — Flushes PHP bytecode cache so the new PHP files are compiled fresh
- Theme Cache — Clears the ThemeManager's internal registry
- Settings Cache — Reloads autoloaded settings (including new Theme Options)
- Template Cache — Refreshes the TemplateResolver's view path resolution
This eliminates the common issue where new Theme Options or views do not appear after a theme update.
Admin UI
The Cache Management page is accessible at Settings Hub → System → Cache (or directly at /admin/settings/cache). It provides:
- Cache driver info at the top (file, database, redis, etc.)
- Grouped cache type cards organized by Laravel, PolyCMS, Server, and Module
- Status badges showing compiled view count, cached/not-cached states, OPcache hit rate
- Individual clear buttons per cache type with loading states
- "Clear All Caches" master button
- Handler badges showing which module manages a specific type
- Full dark mode support
Best Practices
-
Use
cache.type.handlerfor conflict prevention. If your module takes over a cache type, always register a handler so core and other modules know not to interfere. -
Keep
cache.clear.{type}handlers fast. The admin expects cache clearing to be near-instant. For slow operations (like CDN purges), consider queuing the job and returning immediately. -
Always return
$handlerin the filter chain. If you are checkingcache.type.handlerfor a specific type, always return the original$handlerfor types you do not manage. -
Group as
module. When registering custom cache types, use'group' => 'module'so they appear in the "Module Extensions" section of the admin UI. -
Log cache events. Use
cache.clearingandcache.clearedfor audit trails, especially in production environments.
Related Resources
- Hooks & Filters: Extending the Core — Learn the fundamentals of the PolyCMS Hook System.
- Core Hooks & Filters Reference — Complete reference of all ~90 hooks available in the core.
- Module Development: Getting Started — How to create a PolyCMS module from scratch.