Download the PHP package langsys/laravel-request-query-cache without Composer
On this page you can find all versions of the php package langsys/laravel-request-query-cache. It is possible to download/install these versions without Composer. Possible dependencies are resolved automatically.
Download langsys/laravel-request-query-cache
More information about langsys/laravel-request-query-cache
Files in langsys/laravel-request-query-cache
Package laravel-request-query-cache
Short Description Request caching toolkit for Laravel: per-request Eloquent query deduplication (firstCached/getCached) plus idempotent HTTP responses via the idempotent middleware.
License MIT
Informations about the package laravel-request-query-cache
Laravel Request Query Cache
A request caching toolkit for Laravel with two independent features:
- Per-request query deduplication —
firstCached()/getCached()macros that run a query once per request and serve identical repeats from an in-memory store flushed when the request ends. (Not a persistent cache.) - Idempotent HTTP responses — an
idempotentmiddleware that replays the stored response for a repeatedIdempotency-Keyinstead of executing the route again. (Persistent: uses your configured cache store.)
The two share nothing but the package — pick either, or both. Query dedup needs no config; idempotency is opt-in per route.
Why would I want this?
The single best use case is a query you run to validate input that you then need again downstream.
Validation rules and controllers naturally re-express the same query. A rule fetches a row to check it exists / is in the right state; then the controller (or service) fetches that same row to actually do the work. That's two identical round trips to the database for one logical lookup.
The usual workarounds are awkward: smuggle the already-fetched model out of the
rule into the controller, or skip the rule and re-validate inline in the
controller. With firstCached()/getCached() you don't have to. Both layers
just write the natural query — identical SQL + bindings hit the database once,
and the controller gets the row the rule already loaded.
The goal: zero validation in the controller/service layer — validation stays in the rule where it belongs, and the controller reuses the query for free.
Example: a custom rule and a controller sharing one query
A vanilla Laravel validation rule that runs a query:
The controller validates, then reuses the exact same query — no second DB hit, no model smuggled out of the rule, no inline re-validation:
The rule has already done the DB work; the controller's query resolves from the
in-memory store. The only requirement is that both queries are identical — same
where/whereNull clauses in the same order, so they produce the same SQL and
bindings.
Installation
The service provider is auto-discovered. No configuration required.
Usage
If the same query (identical SQL and bindings) runs again during the same request, it returns the stored result without touching the database.
Different queries are cached independently — bindings are part of the cache key,
so where('id', 1) and where('id', 2) never collide.
How it works
- A
RequestQueryCachesingleton holds an in-memoryarraykeyed bymd5(sql + serialized bindings). getCached()wraps->get();firstCached()wraps->first()(and prefixes its key withfirst:so the two never collide on the same query).- The store is flushed on
app.terminating(covers PHP-FPM) and on Octane'sRequestReceivedevent when running under Laravel Octane, guaranteeing every request starts with an empty store.
Caveat: writes within the same request
Because results are memoized on SQL + bindings, if you write to a row and then
re-query it with firstCached()/getCached() in the same request, you get
the pre-write cached value. Use the uncached first()/get() after a write you
need to read back in-request.
Idempotent HTTP responses
State-changing endpoints (payments, orders, sign-ups) get retried — by impatient
users, flaky networks, and queue workers. The idempotent middleware makes those
retries safe: the client sends a unique Idempotency-Key header, and any repeat
of that key replays the original response instead of running the route twice.
How a request is handled
- Only
POST/PUT/PATCHare guarded (configurable). Everything else passes through. - No key present →
400if the route requires it, otherwise passes through. - Key seen before, same request → the stored response is replayed.
- Key seen before, different request body →
422(the key was reused for something else — a client bug you want surfaced, not silently mishandled). - Key currently in flight (a concurrent duplicate) →
409+Retry-After: 1. An atomic lock guarantees the route body runs at most once even under a simultaneous double-submit.
A request's identity (its fingerprint) is the HTTP method + route + path
parameters + query string + body. Body field order doesn't matter —
{"a":1,"b":2} and {"b":2,"a":1} are the same request. Reusing one key across
different path parameters (e.g. /projects/A vs /projects/B) is treated as
misuse and returns 422. The key is namespaced by scope so two callers can
use the same key without colliding.
Scopes — user | ip | global | apikey:
user— per authenticated user; falls back to the request IP when there is no session user.ip— per client IP.global— one key space shared by everyone.apikey— per tenant for API-key auth (where there is no session user). It reads the request attribute named byscope_attribute(defaultapi_key_id), which your auth middleware sets, and falls back to user/IP when that attribute is absent:
Per-route overrides
Override ttl, required, and scope inline — idempotent:{ttl},{required},{scope}:
Configuration
Defaults work out of the box. To customize, publish the config:
TTL guidance: default to 24h for money/order endpoints (a retry hours later must not double-charge — same window Stripe uses); drop to minutes for cheap, high-volume endpoints.
Requirements & caveats
- The cache store must support atomic locks —
redis,memcached,dynamodb,database,file, orarray. Set a specific store via thestoreconfig key if your default doesn't. (If the store can't lock, the middleware still replays but loses the concurrent-duplicate409guarantee.) 5xxresponses are never stored — a transient server error must re-execute on retry, not replay forever.2xx–4xxresponses are stored.- Body-sensitive by design — reusing a key with a changed payload is a
422, not a silent overwrite. That's the safety guarantee. - Streamed/binary (non-string-body) responses are passed through unstored.
The middleware also exposes $request->attributes->get('idempotent') (bool) and
'idempotency-key' to downstream code.
Note: attribute-style usage (
#[Idempotent]on a controller method) is not wired up yet — use theidempotentmiddleware alias or class for now.
Testing
License
MIT
All versions of laravel-request-query-cache with dependencies
illuminate/database Version ^10.0|^11.0|^12.0
illuminate/support Version ^10.0|^11.0|^12.0