barchart/laravel-remember-all
A Laravel package that remembers all query string parameters across requests, making it easy to preserve filters, sorting, and pagination as users navigate away and back.
Install and pin. composer require barchart/laravel-remember-all — but first open the package's composer.json and confirm two things: the PHP floor (^8.0?) and whether the Laravel constraint is additive (^8.0|^9.0) or displacing (^9.0 only). The package is archived as of the 2022-09-30 release, so on Laravel 10+ expect resolution to fail outright. Treat it as an L8/L9-only dependency unless you vendor/fork it.
Look here first, in order:
config/remember-all.php after php artisan vendor:publish — this is where TTL, cache store, and the global on/off switch live. Confirm the publish tag name against the provider; the package ships with a service provider that merges this config.rememberAll() exists everywhere) or a model trait. The description mentions both paths, so confirm which one your version actually exposes.First use case. Pick one genuinely expensive, read-heavy query: a public listing page, a dashboard aggregate, or a slow lookup table. Then:
Post::query()
->where('published', true)
->orderByDesc('published_at')
->rememberAll()
->get();
Verify it worked by watching the query log on two consecutive requests — the second should show no SQL for that query. If it still fires, the cache store, TTL, or global switch is misconfigured; check config/remember-all.php before blaming the chain.
Do not adopt globally on day one. Enable per-query first, prove the hit rate and staleness behaviour on one endpoint, then consider the global switch.
Per-query opt-in (default, safest). Chain rememberAll() on the builder just before the terminal method (get, first, paginate, value). This is the drop-in replacement for scattered ->remember(60) calls and keeps the blast radius to queries you deliberately chose.
Per-model trait (targeted global). Apply the trait to read-mostly models — reference tables, category trees, CMS content — so every query on that model is cached without touching call sites. This is the sweet spot: cached by domain, not by application.
Global opt-in via config (highest risk, highest leverage). Flip the config flag and every query builder result is cached. Only do this once you have confidence the package ignores non-SELECT statements (verify this — if it does not, you will cache nothing useful and potentially wrap write paths). A safer staging approach is to enable global mode on a single read-only service worker or a dedicated read replica connection, rather than the whole app.
Placement in the chain. rememberAll() is a builder-level macro, so the cache key is derived from the SQL and bindings as constructed at call time. Putting it before conditional scopes (when(), whereHas) means the key reflects the full resolved SQL — good. Putting it after a ->limit() you vary at runtime is fine too, but be consistent: two call sites that build logically identical queries in different orders produce different keys and won't share cache entries.
TTL strategy. Set the config TTL to the shortest staleness you can tolerate for the fastest-changing cached model, then override per query if the package supports a duration argument (->rememberAll(300)). Short TTLs (30–300s) are usually the right default because the package has no write-invalidation hook — the TTL is your invalidation policy.
Explicit invalidation, if you need it. Since keys are hashes of SQL + bindings, you cannot cheaply derive them by hand. Two workable patterns:
saved/deleted of the model, call Cache::flush() or Cache::forget() on a known key if you've derived it. Flushing the whole store is heavy-handed — acceptable for small apps, not for shared Redis.cache_version integer in your where clause and bump it on write. The SQL changes, so the key changes, and the old entry expires naturally. This is the cleanest workaround for the missing invalidation API.Eager loading. with('comments') runs a second query — the parent query is cached, the relation query is not, unless the package hooks the relationship builder too. Do not assume with() output is frozen; profile it. If relation queries dominate your load, cache the assembled result at the Cache::remember layer instead.
Testing pattern. Global caching will poison test suites across tests. In phpunit.xml or a base TestCase::setUp, disable the package (config flag) or set the cache store to array. RefreshDatabase does not clear the cache — if you leave caching on, add Cache::flush() in setUp. Add one integration test that asserts a second identical call hits the cache (query count assertion), so a package/config regression fails loudly.
Archived is the headline risk. No Laravel 10/11 support, no security patches. If you need it long-term, fork it into your own namespace and own the constraint — that is cheap for a package this small, and it removes the upgrade dead-end. Do not let it become an un-forked transitive dependency.
No write invalidation. Writes do not evict cache entries. Any read-after-write flow (form submit → redirect → show) will serve stale data until TTL expiry unless you version keys or flush. This is the single most common production surprise.
Cache-key sensitivity. Keys are generated from SQL text + bindings. Whitespace, clause order, and binding types all matter: where('id', 1) and where('id', '1') may hash differently depending on how bindings are normalised. Centralise query construction in scopes/repositories so keys stay stable across call sites.
Multi-tenancy hazard. The key does not know about your tenant. Two tenants running the same SQL with the same bindings share a cache entry, which is a data-leak bug waiting to happen. If you are multi-tenant, either put tenant_id in every cached query's where clause (so it lands in the bindings) or do not use this package on tenant-scoped models.
Pagination and counts. paginate() caches the page's model collection, but the count() query and the page links are computed separately. You get partial caching — good enough for a listing page, misleading if you assume the whole paginator is frozen.
Payload size. Cached Eloquent collections serialise every model. A 10k-row result set will be a fat Redis value and a slow unserialise. Use rememberAll() for bounded result sets and skip it on unbounded exports. Prefer Redis or Memcached over the file store — file caching of large serialised collections is where this pattern usually falls over.
Config caching. php artisan config:cache bakes the TTL and store into the compiled config. Changing config/remember-all.php without re-running it (or clearing bootstrap/cache) silently does nothing. Same applies in CI.
Debugging. Confirm hits with DB::listen in a local tinker session or Laravel Telescope — a cached query produces zero events. If you need to inspect a key, log the SQL + bindings and hash them the same way the package does, or add a temporary Cache::get() probe. There is no first-class debug mode.
Extension points. The config file is the supported extension surface. Beyond that, fork — the package is small and unmaintained upstream, so subclassing the macro or service provider is more fragile than a straight fork. When you fork, keep the namespace change minimal so you can diff against upstream if it ever revives.
How can I help you explore Laravel packages today?