Download the PHP package kai-init/laravel-normcache without Composer
On this page you can find all versions of the php package kai-init/laravel-normcache. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download kai-init/laravel-normcache
More information about kai-init/laravel-normcache
Files in kai-init/laravel-normcache
Package laravel-normcache
Short Description Normalized caching for Laravel Eloquent. Self-invalidating, Redis-backed. Caches query IDs and model entities separately with versioned invalidation.
License MIT
Informations about the package laravel-normcache
Laravel Normcache
Normalized caching for Laravel Eloquent. Self-invalidating, Redis-backed.
Most caching packages store each query result as one serialized collection. Normcache takes a different approach: a query cache only stores the matching IDs, while each model's attributes live in their own key. The same model can appear in many cached queries but is only stored once, so a single version bump invalidates everything that returned it, in O(1).
Requirements: PHP 8.2+, Laravel 11/12/13, Redis 4.0+
Table of Contents
- Installation
- Usage
- Cache Bypasses
- Limitations
- Configuration
- Observability
- Redis Clustering
- Octane & Horizon
- Performance
- License
Installation
Add the Cacheable trait to any model you want cached:
Usage
Basic Queries
Bypassing the Cache
Cross-Table Queries
Queries that span multiple tables are not cached by default — Normcache can't infer which model writes should invalidate them. dependsOn() lets you declare the dependency explicitly:
All dependsOn queries are cached as versioned raw rows. When any declared model class is written, the versioned key becomes unreachable and the next read re-populates from the database. Pessimistic locks always bypass the cache.
List every table the query reads. An under-declared dependency means silent staleness until TTL. Use tag() / flushTag() when you need manual invalidation for events the model version system cannot see.
Per-Query TTL
Aggregates
withCount, withSum, withAvg, withMin, withMax, and withExists are cached automatically and invalidated when related rows change.
Relationship Caching
BelongsTo, BelongsToMany, MorphTo, MorphToMany, MorphedByMany, HasManyThrough, and HasOneThrough are cached for eager loads — on a warm hit no SQL is executed. HasOne, HasMany, MorphOne, and MorphMany are cached via the query cache when the related model uses Cacheable.
attach, detach, sync, and updateExistingPivot automatically invalidate the relevant pivot cache.
Manual Flush
If you mutate cacheable tables outside Eloquent, flush manually after the write:
Tag-Based Flush
Tag any query to group cache entries for manual flushing — useful for invalidation events the version system can't see (deploys, config changes, nightly rebuilds):
Post::on('replica')is not supported. Use distinct model classes with a fixed$connectionfor per-connection cache isolation.
Cache Bypasses
| Query feature | Workaround |
|---|---|
Pessimistic locking (lockForUpdate / sharedLock) |
None — must hit DB |
| Inside a database transaction | None — must hit DB |
Raw SQL / DB::table(...) |
None — flush manually |
Everything else — JOIN, GROUP BY, DISTINCT, subquery WHERE, raw ORDER BY, calculated columns — is cacheable with dependsOn().
Limitations
- Normcache only hooks Eloquent models that use the
Cacheabletrait. Query builder calls such asDB::table(...),DB::select(), andDB::statement()are never cached. - Writes outside Eloquent are invisible to the model version system. Flush the affected model or tag manually after imports, raw updates, maintenance jobs, or external syncs.
- Dynamic connection switching (
Post::on('replica')) is not supported. Use separate model classes with fixed$connectionvalues when the same table is read through multiple connections. dependsOn()is explicit by design. If a query reads another table, include that model class or manually flush a tag that covers the query.- Models are expected to use standard single-column primary keys.
- Packages that replace Eloquent builders, relation classes, or hydration behavior may bypass parts of Normcache.
Configuration
cooldown — Consecutive writes within the window bump the version only once. Useful for write-heavy models.
fallback — When true, Redis exceptions are caught, the cache is disabled for the request, and queries fall back to the database.
events — Set to false to disable hit/miss event dispatches on hot paths.
fire_retrieved — When true, models hydrated from Redis fire Eloquent's retrieved event (disabled by default).
Observability
Laravel Debugbar
When fruitcake/laravel-debugbar is installed, enable the Normcache collector:
This adds a Normcache timeline tab showing every query hit, miss, bypass, and model fetch — with key, kind, and duration — for the current request.
Events
| Event | Fired when | Properties |
|---|---|---|
QueryCacheHit |
Cached query result served from Redis | modelClass, key |
QueryCacheMiss |
Query not cached — DB queried | modelClass, key |
ModelCacheHit |
Model attributes served from Redis | modelClass, ids[] |
ModelCacheMiss |
Model attributes not cached — DB queried | modelClass, ids[] |
Redis Clustering
Every key uses a hash tag derived from the model class — {posts}, {analytics:posts} — so all keys for a given model land on the same cluster slot. MGET batches, Lua scripts, and pipelines never cross node boundaries.
Enable with 'cluster' => true in the config. flushAll() is supported.
Octane & Horizon
Works out of the box. State is reset between Octane requests and queue jobs — including re-enabling the cache if a Redis error disabled it mid-job.
Performance
- Single round trip on cache hit — version check + ID fetch + model
MGETin one LuaEVAL. MGETfor bulk reads — all model attributes for a result set in one Redis call.- No scanning on invalidation — version bump makes stale keys unreachable; TTL handles eviction.
- Stampede protection — waiters
BRPOPa wake channel (200ms) instead of storming the DB. Requires Redis 6.0+ for sub-second precision. - igbinary support — smaller payloads and faster serialization when the extension is installed.
License
MIT