Product Decisions This Supports
- Build vs. buy for read-path performance. This package remains a candidate default for "make Eloquent reads fast without a redesign." A PM can scope adoption as a one-sprint effort (composer install, trait on hot models, Redis config, TTL policy) versus a multi-quarter build of a custom caching layer with bespoke invalidation. That comparison belongs in roadmap planning, not in an engineering side quest.
- Freshness policy as an explicit product decision — now configurable. 13.2.0 adds global and per-model TTL caching. Staleness stops being an implicit property of "we cached it" and becomes a dial a PM can set per domain: seconds for a dashboard counter, minutes for a catalog listing, longer for reference data and tenant config. This is the single most PM-relevant change in the release, because it lets product owners set the freshness/latency trade-off themselves instead of accepting engineering's uniform default.
- Capacity and cloud-cost planning. Read-result caching on read-heavy endpoints is a direct lever on database CPU, replica count, and managed-DB spend. Use it to defer a vertical/horizontal database upgrade, or to hit a cost-reduction OKR without a re-architecture. Note that this release deliberately stops caching some query shapes (unkeyable
where()/having() values, afterQuery() constraints, relations to non-cacheable models) and tightens keying elsewhere — so measured hit rates and savings may come in slightly below a 13.1.x baseline. Set expectations before quoting a cost number.
- SLA and latency targets. It supports committing to p95/p99 goals for endpoints dominated by repeated, similar Eloquent reads — catalog pages, dashboards, feeds, reference data, tenant config, permission lookups — where the same queries recur per request or per user.
- Roadmap sequencing. It is a drop-in trait rather than a data-layer rewrite, so it can be scheduled as a low-risk increment ahead of larger investments (read replicas, CQRS, denormalization, search indexes). It buys runway while the bigger item stays on the roadmap.
- Feature viability for new surfaces. "Can we afford to ship this dashboard/report/API endpoint against the current database?" becomes answerable with a cheaper yes, unlocking feature bets that would otherwise be blocked by infrastructure cost.
- Use-case triage. It cleanly serves read-heavy, write-light, staleness-tolerant domains. A PM should say yes to those and explicitly no to write-heavy or strongly consistent workflows, rather than letting one caching tool be applied uniformly. Per-model TTL makes that triage enforceable in config rather than a convention people forget.
- Data-correctness posture (changed this release). 13.2.0 is substantially a correctness release: several keying gaps could let a query read rows written under a key another query shared, and pivot-table writes now flush the pivot tags that many-to-many results depend on. Practically, this means teams on 13.1.x may be exposed to wrong-result risk on those query shapes. Upgrading should be framed to stakeholders as a correctness fix with a performance benefit, not a routine bump.
- Platform baseline risk. The release requires PHP 8.3+; PHP 8.2 projects stay pinned at 13.1.11 and therefore do not receive the correctness and invalidation fixes. That splits the fleet into "fixed" and "unfixed" cohorts and turns a PHP-version upgrade into a prerequisite for a data-correctness fix — a scheduling item for the platform roadmap, not a footnote.
- Incident and risk posture. Cache key and tag formats are effectively a public contract, and this release changes keys and tags across a long list of query shapes (global-scope closures,
morphTo eager loads, many-to-many with pivot options, subquery from, macro/builder-method arguments, cool-down keys on $cachePrefix models). Upgrades cold-start caches and shift which results an invalidation reaches. "Cache contract stability" is a line item in release/upgrade planning and a standing reason to pin versions and stage rollouts.
- Operational blast radius of tooling (improved).
modelCache:clear no longer wipes the whole Redis store or the whole Redis database when the prefix is empty; it deletes only the package's entries. That removes a serious footgun from runbooks — but any script that relied on the old behavior to clear everything now silently under-clears and needs php artisan cache:clear alongside it. Audit automation before assuming the command is unchanged.
- Dependency and OSS-risk review. Still MIT-licensed with a maintained release cadence and an active review process on complex cache-key work. Before betting a performance or correctness OKR on it, verify Laravel-version support and real-world adoption; treat registry-dependent counts as artifacts to check, not evidence.
When to Consider This Package
- Read-heavy, low-churn domains. The same Eloquent queries repeat across requests or users, and writes to those models are infrequent relative to reads.
- Tolerable staleness — and now explicitly bounded. The product can accept a short stale window after a write, and you are prepared to express that window as a per-model TTL rather than hope for the best. No strict read-after-write requirement on cached surfaces.
- A taggable cache store is already available. Redis or Memcached is in the stack (or can be), since tag-based invalidation depends on it; teams on file/database cache face a different trade-off.
- Eloquent is the access path. Work is concentrated in Eloquent models and query-builder usage the package can observe; raw SQL, stored procedures, and external writers fall outside its invalidation net and need a separate plan.
- Team capacity is constrained. You want the performance win without owning a custom caching and invalidation subsystem, and you value automatic invalidation from model events over hand-written cache-busting code.
- You are on PHP 8.3+ or can get there. New adopters should start on 13.2.x. Teams stuck on 8.2 get 13.1.11 and the older cache-key behavior, which is a defensible short-term position only if none of the affected query shapes (scoped models, polymorphic eager loads, many-to-many with pivot options, macros with arguments) are on your hot paths — verify rather than assume.
- You can instrument it. Cache hit/miss, invalidation, TTL expiry, and query-latency metrics, plus a rollback path (flip the trait off / disable caching per model), exist before broad rollout. Expect hit-rate shifts after this upgrade and compare against a pre-upgrade baseline.
- You can pin and stage. Version pinning, canary rollout, a post-deploy
modelCache:clear, and a cache warm-up plan are feasible, so a key/tag format change is a planned event rather than an outage.
Look elsewhere when:
- Writes are frequent or bursty, or invalidation correctness is business-critical (billing, inventory, entitlements, compliance-sensitive reads) and stale or wrong results carry real cost.
- You need strict consistency, transactional read guarantees, or cross-service invalidation a model-event hook cannot see.
- The bottleneck is not repeated reads — it is missing indexes, N+1 queries, oversized payloads, or an unoptimized schema. Caching masks the problem and adds a second source of truth.
- The dominant win is HTTP-level: full-response caching, CDN, ETags, or edge caching for anonymous, cacheable pages.
- You need multi-region or cross-store coherence, per-field granularity, or cache behavior spanning non-Eloquent data sources.
- The team cannot absorb the operational surface (Redis capacity, eviction policy, key cardinality, cold-start behavior) or cannot commit to upgrade discipline around the cache contract.
- You are pinned to PHP 8.2 and the affected query shapes are on hot paths — fix the runtime first; do not adopt 13.2.x-shaped assumptions against a 13.1.11 install.
How to Pitch It (Stakeholders)
For executives: This is a small, MIT-licensed Laravel package that caches database query results and invalidates them automatically when the underlying models change — an off-the-shelf alternative to building and owning a custom caching layer. The 13.2.0 release is mostly about trust: it fixes cache-key and pivot-invalidation gaps that could return wrong rows in specific edge cases, so upgrading is a correctness action, not just housekeeping. It also adds per-model TTL, which lets product owners choose how fresh each surface must be instead of accepting one global default. Costs to plan for: the release requires PHP 8.3, it invalidates caches once on upgrade (temporary latency spike, then rebuild), and some rarely-hit query shapes now skip caching entirely, so realized savings may be modestly below prior estimates. Engineering effort is a bounded upgrade with a checklist, not a migration. Recommend: get the team onto PHP 8.3 and 13.2.x, then run a bounded pilot on one or two high-traffic, staleness-tolerant surfaces with explicit TTLs, clear success metrics, and a rollback switch before broad adoption.
For engineering: Adopt via the model trait; caching and invalidation are handled for Eloquent reads, with tags doing targeted invalidation where the store supports them. For this upgrade specifically: declare $isCachable as protected bool, delete any trait rule referencing Cachable::newBaseQueryBuilder (Other::newBaseQueryBuilder insteadof Cachable still works), run php artisan modelCache:clear after deploy because some changed queries keep their old key text, and add php artisan cache:clear to any script that depended on modelCache:clear emptying the whole store. Budget for a one-time cache cold start and a hit-rate dip on the affected query shapes; establish a pre-upgrade hit-rate and p95 baseline so the dip is distinguishable from a regression. Treat key and tag formats as a stable public interface — this release changed them across global-scope closures, morphTo eager loads, many-to-many with pivot options, subquery from, withoutGlobalScopesExcept(), beforeQuery() bindings, and macro/custom-method arguments — so format changes are release decisions, not refactors. Note the deliberate trade-off: queries whose values cannot be keyed reliably now always hit the database, which is the right call for correctness but shifts load back onto the database for those shapes. Use per-model TTLs to encode product freshness requirements rather than relying on cache eviction luck, keep raw-SQL and out-of-band writes on a separate invalidation path, and prove behavior with integration tests against a real Redis store — including a pivot-table write flushing many-to-many tags and eager-load TTL resolution — before rollout.