Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Lada Cache Laravel Package

michael-rubel/lada-cache

Lada Cache is a Redis-based automatic query cache for Laravel. It caches Eloquent queries and invalidates entries when related models change, requiring little configuration while speeding up reads and keeping cached data consistent across scalable applications.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal setup

  1. composer require michael-rubel/lada-cache — pin at least ^0.0.2 if your queries use withCount (see Gotchas).
  2. Laravel ≥5.5 auto-discovers the service provider; if not, register LadaCacheServiceProvider in config/app.php.
  3. php artisan vendor:publish --tag=lada-cache-config (confirm the tag in the provider — this package lineage has used more than one).
  4. Provision Redis and add a dedicated connection in config/database.php → redis (own logical DB index, own key prefix). Do not share it with sessions, queues, or Horizon.
  5. Point the DB connection you want cached at the package's driver (typically a lada-cache driver wrapping your real connection), or toggle the enable flag in config/lada-cache.php. It hooks the connection, not your models, so no use Trait step is needed on the Eloquent side.

Where to look first

  • config/lada-cache.php — the whole surface area of the package lives here: Redis connection/prefix, enable flag, logger, and query-tagging behaviour.
  • The service provider — it's how a custom DB driver gets registered; that's the mechanism you're relying on.
  • The README's "how it works" section — read it before you enable globally, because the invalidation model drives every gotcha below.

First use case

A read-heavy endpoint (public dashboard, product listing, API GET that repeats the same 5–10 Eloquent queries). Enable the layer for that connection, hit the endpoint twice, and watch Redis key count plus DB::listen output — second hit should produce zero queries. That's the five-minute proof that justifies or kills the experiment.

Implementation Patterns

Zero-touch retrofit. The main selling point: because it sits at the connection driver, existing controllers/repositories gain caching without edits. This is genuinely useful on a legacy codebase where you can't refactor every query. Enable it on one connection, measure, expand.

Scope it, don't globalise it. Turn caching on per connection (and ideally per app area — read replica, reporting DB, etc.) rather than for the whole application. Write-heavy tables pay an invalidation tax on every INSERT/UPDATE/DELETE; read-only or rarely-changing tables get nearly free wins.

Trust automatic invalidation for model-driven writes. Normal save(), create(), delete() through Eloquent will tag and bust the right rows. This is the happy path and works well for CRUD.

Aggregate loaders are now a supported path — on 0.0.2+. The 0.0.2 release specifically fixed withCount handling, so list endpoints that eager-load counts (Post::withCount('comments')->get()) can be cached without hand-holding. If you have any withCount / relationship-count loading, treat 0.0.2 as your floor and verify with the debugging recipe below before rolling out. Other aggregate shapes (withSum, withAvg, withExists, raw selectRaw aggregates) are not covered by that fix — test each one you actually use.

Pair with HTTP-level caching for hot endpoints. Lada covers the DB layer; a response cache or Cache::remember around the controller return covers the rest. Don't ask the DB layer to solve a whole-response problem.

Test with it off. Add the disable env var to phpunit.xml (<env name="LADA_CACHE_DISABLE" value="true"/>, confirm the exact key in config) so tests don't depend on a Redis instance. Assert query counts with DB::listen in a separate opt-in suite if you want to prove caching works.

Observe before you ship. Turn on the package's logger in staging, sample Redis INFO memory and key counts over a week, and confirm hit rate. If you can't articulate the hit rate, you're carrying invalidation overhead for nothing.

Gotchas and Tips

The maintenance question is still the headline — but marginally less bleak. 0.0.1 → 0.0.2 is one community PR (withCount), not a signal of active stewardship: still a 0.0.x re-publish of spiritix/lada-cache, still near-zero adoption, and the better-maintained descendant remains genealabs/laravel-model-caching. Read the 0.0.2 diff before depending on it; it tells you the query-builder hook can be patched, which is encouraging for the architecture but doesn't answer "who fixes the next bug." If 0.0.2 fails to install on your Laravel/PHP version, stop here and use the maintained fork or plain tagged caching.

withCount was broken before 0.0.2. If you inherited an install on 0.0.1 and saw wrong counts, cache misses on every count query, or invalidation that never cleared a count column, that's the bug. Upgrade and re-verify: cache the count query, write a related row, and confirm the response updates. Note the fix only covers withCount — other aggregate selectors need your own test.

Redis is mandatory. This is not "works with any cache store." No Redis, no package. It also means your cache is in-process-adjacent infrastructure that can disappear — design for a cold cache, not a warm one.

Invalidation granularity is table/row based, and joins can over-invalidate. Complex joins, subqueries, and aggregates may end up flushing far more than the row you changed. On write-heavy tables this produces cache thrash that is worse than no cache. Profile write volume before enabling on those tables.

Raw SQL bypasses everything. DB::statement, DB::unprepared, mass insert(), and update() query-builder calls that skip model events will leave stale rows in cache. Any bulk job, importer, or queue worker needs an explicit invalidation call afterwards — this is the #1 source of "works in dev, stale in prod."

Transactions and long-running workers. Invalidation timing relative to commit is the thing to verify on your version. A read inside a long transaction, or a queue worker holding a stale cache across jobs, can serve old data. Keep transactions short and don't cache across tenant boundaries.

Multi-tenancy: isolate your prefixes. Row tags are keyed by ID, so if two tenants share a Redis prefix and DB, you can leak data across them. Give each tenant (or at least each environment) its own logical DB + prefix.

Debugging recipe. Enable the package logger → inspect Redis keys to see which tags a cached key carries → reproduce the write → confirm the tag set was emptied. If invalidation didn't fire, the write bypassed Eloquent; if it fired too broadly, the query joined too much. For aggregate queries, add a step: dump the generated SQL for the withCount/aggregate query and confirm it's being tagged at all rather than silently skipped.

Low lock-in is a real plus. Because it's a connection-driver swap, removal is a config change plus a Redis flush — no code to unwind. Treat that as your escape hatch and set a review date (say, one quarter) to decide whether the hit rate justifies keeping a near-unmaintained 0.0.x dependency.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
shitoudev/permissionmanager
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console