Centralized Cache Management in Laravel Using Configurable Keys and CacheBuilder
This article explains how to introduce a centralized caching system in a Laravel application by using the Cache facade, defining configurable cache keys in a dedicated config file, and creating reusable CacheBuilder and SiteCache classes to simplify key management, improve performance, and enhance maintainability.
In my Laravel project, the urgent need for performance optimization and reduction of database queries made me realize that a caching mechanism must be introduced.
Cache::remember(
'user_data_{$id}',
now()->addMinutes(60),
fn () => User::find($userId)
);The power of Cache::remember lies in its ability to retrieve data from the cache when available, or regenerate it via a closure, thereby improving performance. However, managing unique keys—especially when concatenating other data such as model IDs—poses a challenge.
Cache::remember(
'site_page_{$machine_name}_{$slug}_{$language}',
now()->addMinutes(60),
fn () => SiteService::getContent($machine_name,$slug,$language)
);To address this issue effectively, I chose to leverage Laravel's configuration files.
return [
'default' => env('CACHE_DRIVER', 'file'),
'prefix' => env('CACHE_PREFIX', Str::slug(env('APP_NAME', 'laravel'), '_').'_cache_'),
'keys' => [
'users' => 'users',
'user' => 'user_%s',
'view_page'=> 'site_%s_%s_%s',
]
];This approach promotes key retrieval without resorting to magic strings, providing clarity and maintainability.
$viewPageKey = config('cache.keys.view_page');
Cache::remember(
sprintf($viewPageKey, $machine_name,$slug,$language),
now()->addMinutes(60),
fn () => SiteService::getContent($machine_name,$slug,$language)
);
// or
$userKey = config('cache.keys.user');
Cache::remember(
sprintf($userKey, $userId),
now()->addMinutes(60),
fn () => User::find($userId)
);Nevertheless, as the system continuously expands, adjusting cache configurations for each module becomes increasingly cumbersome. To solve this, I employed the CacheBuilder class within the Core or Support modules, designing a flexible and efficient solution that simplifies cache configuration adjustments while enhancing scalability and maintainability.
<?php
declare(strict_types=1);
namespace Modules\Support\Builder\Cache\V1;
use BadMethodCallException;
use Closure;
use DateInterval;
use DateTimeInterface;
use Illuminate\Support\Facades\Cache;
use ReflectionClass;
use function cache;
class CacheBuilder
{
private string $key;
private mixed $data;
private Closure|DateInterval|DateTimeInterface|int|null $ttl;
private mixed $tags;
public function __construct(string $key, DateInterval|DateTimeInterface|int|Closure|null $ttl = null)
{
$this->ttl = $ttl;
$this->key = $key;
}
public static function __callStatic($property, $arguments): self
{
$constant = self::getConstant($property);
if (null !== $constant) {
$key = $constant instanceof Closure ? $constant(...$arguments) : vsprintf($constant, $arguments);
return new static($key);
}
throw new BadMethodCallException("Property {$property} not supported.");
}
private static function getConstant(string $property): ?string
{
$reflection = new ReflectionClass(static::class);
$constants = $reflection->getConstants();
$matchedConstant = array_filter($constants, function ($constant, $name) use ($property) {
$propertyName = mb_strtolower(preg_replace('/(?<!^)[A-Z]/', '_$0', $property));
return mb_strtolower($name) === $propertyName;
}, ARRAY_FILTER_USE_BOTH);
return current($matchedConstant) ?? null;
}
public function getKey(): string
{
return $this->key;
}
public function data(mixed $data): static
{
$this->data = $data;
return $this;
}
public function ttl(Closure|DateInterval|DateTimeInterface|int|null $ttl): static
{
$this->ttl = $ttl;
return $this;
}
public function tags(mixed $tags): static
{
$this->tags = $tags;
return $this;
}
public function get(): mixed
{
return Cache::get($this->key);
}
public function forget(): bool
{
return Cache::forget($this->key);
}
public function forever(): bool
{
return $this->performCacheOperation('forever');
}
public function remember(): mixed
{
return $this->performCacheOperation('remember', [$this->ttl, $this->data]);
}
public function put(): bool
{
return $this->performCacheOperation('put', [$this->ttl]);
}
protected function performCacheOperation(string $method, array $additionalArgs = []): mixed
{
$cache = cache();
if ($cache->supportsTags() && !empty($this->tags)) {
$cache = $cache->tags($this->tags);
}
return $cache->$method($this->key, ...$additionalArgs);
}
}In the CacheBuilder class, the __callStatic method is used to dynamically handle static method calls. It parses class constants to respond to these calls, making the instantiation of CacheBuilder more concise. This mechanism allows predefined constants or custom formatted options to flexibly configure the behavior of CacheBuilder instances.
Subsequently, I carefully designed and implemented a cache manager for each module, the SiteCache class, which extends CacheBuilder and provides a set of static methods dedicated to handling cache operations with preset keys. This modular construction not only reduces the complexity of cache management but also ensures consistency and maintainability of the solution.
<?php
declare(strict_types=1);
namespace Modules\Site\Cache\V1;
use Modules\Support\Builder\Cache\V1\CacheBuilder;
/**
* @method static Domains()
* @method static SiteCache Domain(string $domain)
* @method static SiteCache Attributes(string $site_id, string $attribute_name)
* @method static SiteCache ViewPage(string $site_machine_name, string $slug, string $language)
*/
class SiteCache extends CacheBuilder
{
private static int $ttl = 60;
public const DOMAINS = 'site_domains';
public const DOMAIN = 'site_domain_%s';
public const ATTRIBUTES = 'site_%s_%s';
public const VIEW_PAGE = "view_page_%s_%s_%s";
}The SiteCache class extends CacheBuilder, defining several static methods that use predefined keys to facilitate specific cache operations. These methods make creating SiteCache objects more straightforward while guaranteeing consistent key names and TTL values. Using SiteCache, developers can easily manage and access site‑related cached data.
SiteCache::ViewPage($machine_name, $slug, $language)
->ttl(now()->addMinutes($entity->cache_ttl))
->data(function () use ($entity, $language) {
$content = $this->getEntityContent($entity, $language);
return $content;
})
->remember();
// or
SiteCache::Domains()
->tags(["sites"])
->data(fn () => Site::all(['domain'])->pluck('domain')->toArray())
->ttl(now()->addMinutes(self::$cacheDuration))
->remember();
// or
SiteCache::Domain($siteId)->data($site)->forever();By adopting this strategy, I eliminated the need for scattered magic strings, establishing a centralized and intuitive cache management system. All critical modifications are now performed in a single location, simplifying maintenance and significantly improving code readability.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
php Courses
php中文网's platform for the latest courses and technical articles, helping PHP learners advance quickly.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
