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

Laravel Modelcache Laravel Package

foxws/laravel-modelcache

Cache Eloquent model queries and results with a simple, fluent API for Laravel. Reduce database load by storing frequently accessed model data in your cache store, with automatic invalidation on updates. Lightweight, easy to configure, and works with any Laravel cache driver.

View on GitHub
Deep Wiki
Context7

Technical Evaluation

Architecture fit. The package is a thin convention layer over Laravel's cache contracts — a trait (InteractsWithModelCache), a ModelCache facade, a CacheProfile policy object, and a config file. It adds no persistence engine, no queue worker, no schema, and no runtime service. That is a favorable shape for a TPM: the surface we are adopting is small, the blast radius is bounded by the call sites we write, and the exit cost is a use statement per model plus the keys we chose to create.

Fit is good when the product need is small per-model scalars that are nice to keep but not worth a row: playback/resume position, in-progress wizard step, last-viewed tab, an A/B seed, a memoized stat block on a detail page. It converts "we need a table, an index, and a backfill" conversations into one-day stories. Fit is poor when our caching conventions already live in a repository/service layer — the package attaches caching to the model, which creates a second idiom in code review and a second place to look when a value is stale.

Integration feasibility. Low effort, high judgment. Install is one composer require, optional config publish, three env vars (MODEL_CACHE_ENABLED, MODEL_CACHE_STORE, MODEL_CACHE_LIFETIME), and a trait on target models. No migrations, no manual provider registration, no external dependency. The entire engineering cost concentrates in one decision: classifying each value as user-scoped or shared. Keys are suffixed with the authenticated user ID by default, so a value written during a user's request is invisible to guests, other users, console commands, and queued jobs. Overriding cacheNameSuffix() to '' is a small code change that flips a performance feature into a cross-user exposure risk when applied to genuinely per-user data. That classification must be made explicitly before the first PR, not discovered in review.

Technical risk. The dominant risk is the maturity signal, not the code: 2 GitHub stars, 0 dependents, single-maintainer provenance, no downstream validation. That is a bus-factor and long-term-support risk, and it should drive our posture (vendor, pin, budget for a fork) more than any architectural concern. Secondary risks worth naming to stakeholders:

  • The documentation contradicts the reviewed behavior. The package description advertises "automatic invalidation on model create, update, and delete" and "Eloquent query caching." The guidance and reviewed behavior say the opposite: no invalidation hooks, not a query cache, not for storing models or collections. No roadmap commitment may be made against invalidation or query caching until source is read and the question is settled. This single ambiguity gate is worth more diligence than the rest of the evaluation combined.
  • Silent cache-off. MODEL_CACHE_ENABLED=false disables everything with no error. Every read path needs a working default (modelCached($key, $default)), or the kill switch becomes an outage switch.
  • Store volatility. With array or file stores, values vanish on deploy or flush and the feature looks broken in staging. Any value treated as authoritative needs a persistent store — and usually the cache may not be the system of record at all.
  • Stale semantics. Keys are model + name. If we change the meaning or shape of playback_position, stale values can live up to the one-week default TTL. Version the key name from day one.
  • Provenance anomaly. The listed last release date (2026-10-14) is in the future relative to any plausible review date. Treat version metadata as unverified and confirm tags and diff history directly before pinning.

Key questions.

  1. What is the maintainer's support commitment, and is release history active or a single burst?
  2. Which store do we intend, and does it survive deploys and flushes?
  3. For each candidate value: user-scoped or shared, TTL, and is the cache permitted to be the system of record? (Default answer: no.)
  4. Do we need the CacheProfile extension point, or is the trait default sufficient for the first release?
  5. Are the swappable hasher/serializer compatible with our existing key-prefix and tenancy conventions?
  6. Does anything already in the product depend on the invalidation behavior the description claims? If so, close that gap with observers before adoption.
  7. Is the surface small enough to vendor or reimplement if abandoned? (Yes — a handful of files, MIT.)

Integration Approach

Stack fit. Works on any Laravel app with a configured persistent cache store and a meaningful authenticated-user context on the cached models. It composes with multi-tenant models because the suffix is per-model overridable, so a tenant-scoped suffix is a small extension rather than a fork — but note that it customizes the one thing the package is opinionated about, which means we own that behavior permanently. It does not fit where there is no stable auth context (the default suffix degenerates toward the shared namespace) or where caching is already centralized in a service layer.

Migration path. Purely additive and reversible. Add the trait, start writing new keys, and no existing read path changes until a call site explicitly reads through modelCached. No data migration. Rollback is removing call sites and the trait; cached entries become orphaned keys that expire on their TTL. The only migration-shaped task is a key classification inventory: for each value, record model, key name, owner (user vs. shared), TTL, store, and system-of-record yes/no. That should be a deliverable owned by a named person, not a wiki page — it is the artifact that lets a future owner reason about this without re-reading the source.

Compatibility. Verify the Laravel constraint in composer.json explicitly rather than inferring it from a release date we already can't trust. Set MODEL_CACHE_STORE explicitly instead of letting it fall back to CACHE_STORE; otherwise this package's keys compete with session, fragment, and rate-limit keys for the same memory and eviction budget. Confirm the hasher/serializer defaults against our existing key-prefix and tenancy conventions before writing keys, since changing them afterward invalidates everything already stored.

Sequencing.

  1. Gate 0 — Diligence. Someone reads the source (it's small), confirms the actual latest tag and diff history, and settles the invalidation/query-cache contradiction in writing. Nothing else starts until this closes.
  2. Flag and store. Stand up MODEL_CACHE_ENABLED as the single ops kill switch and MODEL_CACHE_STORE pointed at Redis (or database), explicitly not inherited.
  3. Profile ownership. Assign one owner for the CacheProfile contract — TTL, shouldUseCache(), shouldCacheValue() — so squads don't invent key conventions independently.
  4. Pilot. One non-critical, user-scoped feature, one or two keys, working defaults on every read path. Ship behind the flag.
  5. Inventory and expand. Write the key registry, bake version strings into key names, then extend to a second feature only after the pilot has run through a deploy cycle and a cache flush.

Operational Impact

Maintenance. Assume we may be maintaining a fork. The code surface is small enough to read in half a day and reimplement in a few days, which is the main reason this is a defensible adoption despite the maturity signal. Pin the version; don't float. Budget ongoing ownership of the CacheProfile and the key registry as real recurring cost, because unowned keys become ghost data that nobody can safely delete.

Support. There is effectively no upstream support guarantee from a 2-star, single-maintainer package. Internally, the support story is: one named owner for the profile contract, one named owner for the key registry, and a documented exit path (vendor or reimplement) written down before the pilot ships. Escalation for "the cached value looks wrong" must land with the key owner, not with whoever wrote the page.

Scaling. Cost scales as (models × keys × active users) in cache memory, so a per-user default multiplies cardinality quickly — plan Redis capacity and eviction policy before broad rollout, and confirm what happens to our feature when eviction kicks in. modelCacheRemember() on a cold cache will hit the database concurrently for every request until the first value lands; if the wrapped computation is expensive, that's a stampede risk worth a short lock or a warm-up path. The one-week default TTL means both good news (self-healing staleness) and bad news (a semantics change lingers for a week unless key names are versioned). There is no built-in observability — no hit rates, key inventories, per-key ownership, or cost attribution — so any telemetry we want, we build.

Failure modes to design against.

  • Cache disabled or store volatile → feature degrades to defaults, which is fine if defaults exist; broken if any read path assumes presence.
  • Namespace leakage → overriding cacheNameSuffix() to '' on per-user data is a data-exposure incident, not a performance regression. This is the one misconfiguration that requires a review checklist, not just a code convention.
  • Cache-as-system-of-record drift → the package is convenient enough that a feature will eventually treat a cached value as authoritative. Decide the policy up front: pricing, permissions, entitlements, and anything a support agent must reconstruct do not belong here.
  • Store contention → sharing the default store with sessions, fragments, and rate limiters means a cache flush for one concern degrades another.
  • Orphaned keys → removing a call site leaves keys until TTL expiry; acceptable, but the registry should note when a key is retired.

Ramp-up. Small. One engineer, roughly half a day to read the source and the docs, then a PR-sized pilot. The real ramp-up cost is organizational: the per-user-by-default namespace has to be taught once, clearly, or it will be discovered the hard way when a queued job or a guest reads a different namespace than the request that wrote it. Budget for one internal write-up plus one review checklist item on cacheNameSuffix() overrides. The framing for stakeholders should stay narrow and honest: "one less table for state we don't need to keep forever," not "a caching strategy" — and if a feature's correctness depends on the cached value being present, it doesn't belong here.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager