The Symfony Cache Component
The Symfony Cache Component
~16 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
The dashboard statistic from chapter 33 (ProjectStatsService) recomputes on EVERY page load, even though the underlying data often changes LESS OFTEN – time to cache the result.
CacheInterface: Symfony's unified cache abstraction
<?php
declare(strict_types=1);
namespace App\Service;
use App\Entity\Project;
use App\Repository\TaskRepository;
use Symfony\Contracts\Cache\CacheInterface;
use Symfony\Contracts\Cache\ItemInterface;
class ProjectStatsService
{
public function __construct(
private readonly TaskRepository $taskRepository,
private readonly CacheInterface $cache,
) {
}
public function computeStats(Project $project): array
{
return $this->cache->get(
sprintf('project_stats_%d', $project->getId()),
function (ItemInterface $item) use ($project) {
$item->expiresAfter(300); // 5 minutes
return $this->computeStatsWithoutCache($project);
}
);
}
private function computeStatsWithoutCache(Project $project): array
{
// ... exactly the computation from chapter 33 ...
}
}CacheInterface is – EXACTLY like LoggerInterface or MailerInterface – an AUTOWIRED service (block 6). get() implements the "cache-aside" pattern in ONE call: if an entry for this key already exists, it gets returned DIRECTLY; if NONE exists (or it's expired), the callback gets EXECUTED, and the result gets STORED AND returned.
expiresAfter() vs. expiresAt()
$item->expiresAfter(300); // relative: 300 seconds from NOW
$item->expiresAt(new \DateTimeImmutable('tomorrow')); // absolute: a fixed point in timeInvalidating the cache on purpose
If a task changes (chapter 21), the cached statistics value gets STALE – instead of waiting for the 5-minute expiry, let's invalidate it DELIBERATELY:
// In TaskAssignmentService (chapter 36) or wherever tasks get changed:
$this->cache->delete(sprintf('project_stats_%d', $project->getId()));Cache adapters: dev vs. prod
framework:
cache:
app: cache.adapter.filesystem # default: filesystem-based, works everywhere
# Better for production with several servers:
# app: cache.adapter.redis
# default_redis_provider: '%env(REDIS_URL)%'The filesystem adapter works WITHOUT extra infrastructure, but is SERVER-BOUND – with MULTIPLE application servers (chapter 48), server A does NOT see server B's cache. Redis (a separate, shared cache service) solves this for multi-server deployments.
A preview of chapter 46: two different kinds of cache
| Cache type | What it caches |
|---|---|
| This chapter: CacheInterface ("application cache") | Caches ANY value WITHIN your PHP code – like our statistics computation. |
| Chapter 46: HTTP cache | Caches ENTIRE HTTP responses, often BEFORE your application, sometimes WITHOUT PHP even running. |
Tipp: Rule of thumb for cache keys: ALWAYS include every value that affects the result (here: $project->getId()) – a TOO BROAD key (e.g. just 'project_stats' for ALL projects) would wrongly mix up data from DIFFERENT projects.