Skip to content

Caching

This content is for v1.0. Switch to the latest version for up-to-date documentation.

Articulate has three independent cache layers, all using PSR-6 (CacheItemPoolInterface). Pass the same pool instance to share backend, or separate instances for isolation.

Cross-request entity cache. Survives beyond a single EntityManager instance.

Request A: identity map miss → DB hit → entity stored in L2 cache
Request B: identity map miss → L2 cache hit → DB skipped entirely
Request C: identity map miss → L2 cache hit → DB skipped entirely
$em = new EntityManager(
$connection,
resultCache: $cachePool, // also backs L2 cache unless overridden
secondLevelCacheTtl: 3600,
);
// Or with a dedicated L2 pool:
$em = new EntityManager(
$connection,
resultCache: $queryPool,
secondLevelCache: $entityPool, // separate backend for entity cache
secondLevelCacheTtl: 3600,
);

find() checks the identity map first, then the L2 cache, then the database. On flush(), modified and deleted entity entries are evicted automatically — stale data is never served after a write. Eviction also covers sibling entity classes sharing the same table and primary key.

Cache raw result sets from QueryBuilder queries — useful for read-heavy queries that don’t change often.

$users = $em->createQueryBuilder(User::class)
->from('users')
->where('status', 'active')
->enableResultCache(lifetime: 300, resultCacheId: 'active_users')
->getResult();
  • Custom cache key via resultCacheId, or auto-generated from query shape + parameters.
  • Locked queries (FOR UPDATE) are never cached.
  • Call disableResultCache() to opt out per query.

Caches compiled SQL strings (query structure, not results) — eliminates repeated SQL compilation for queries with the same shape but different parameter values.

$em = new EntityManager($connection, statementCache: $cachePool);

Transparent — no per-query opt-in needed. Failures are silently ignored so a broken cache backend never breaks queries.

$connection = new Connection(
dsn: 'mysql:host=127.0.0.1;dbname=myapp',
user: 'root',
password: 'secret',
persistent: true,
);

Skips TCP handshake and authentication overhead on each request. Pair with a pool-aware cache backend for full cross-request performance.