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

Cache Plugin Laravel Package

saloonphp/cache-plugin

Cache plugin for Saloon adds response caching to your API integrations, storing HTTP responses so repeated requests are served from cache instead of hitting the network. Configure cache drivers, TTLs, and per-request rules to speed up your app.

View on GitHub
Deep Wiki
Context7

Getting Started

Install and match versions first. The package tracks Saloon majors: v3.1.0+ of the plugin targets Saloon v4, v3.0.x targets Saloon v3. Pin both in composer.json before you write a line of code — a Saloon upgrade without a plugin bump is the most common way to break a build.

composer require saloonphp/cache-plugin

Where to look first:

  1. src/Contracts/CacheableRequest.php — the interface your request must implement; this is the whole API surface.
  2. src/Drivers/ — pick your storage backend (Laravel cache store, any PSR-16 pool, or a Flysystem disk).
  3. CHANGELOG entry for v3.3.0 — clearCache() lets you purge an entry without sending a request. That single method is what turns this from "TTL cache" into a real invalidation story.

Minimal working setup:

use Saloon\CachePlugin\Contracts\CacheableRequest;
use Saloon\CachePlugin\Drivers\LaravelCacheDriver;

class GetProductsRequest extends Request implements CacheableRequest
{
    public function resolveCacheDriver(): Driver
    {
        return new LaravelCacheDriver(Cache::store('redis'));
    }

    public function cacheExpiryInSeconds(): int
    {
        return 600;
    }
}

Register the plugin on the connector (or per request via withCache()) so the driver is available in the request lifecycle:

protected function defaultPlugins(): array
{
    return [new CachePlugin];
}

First use case: take one slow, idempotent, read-only GET — a product catalog, a reference-data lookup, a config endpoint — wrap it, and measure. Do not start with anything user-scoped or anything that mutates state; get a clean latency/API-call win on a boring endpoint before you argue about invalidation.


Implementation Patterns

Connector-level plugin, request-level intent. Register CachePlugin once on the connector, then let each request opt in by implementing CacheableRequest. This keeps the connector honest (one place to swap the driver) while cacheability stays a property of the request — which is exactly where reviewers will look for it.

Scope the key before you set the TTL. The default key is derived from the URL (query string included since v0.3.0). For anything tenant-, user-, or locale-dependent, override the key method on the request:

public function getCacheKey(): string
{
    return sprintf('products:%s:%s', tenant()->id, $this->query()->get('page', 1));
}

If different inputs produce the same URL — POST bodies, Authorization headers, Accept-Language — the key must include them, or you are serving someone else's data. The v2.1.0 release specifically opened up body-in-key support; use it.

Two invalidation flows, two methods:

Need Method Behaviour
Force a refresh on next call $request->invalidateCache() Purges, then sends the request and re-caches
Purge only, no outbound call $request->clearCache() (v3.3.0+) Deletes the entry, sends nothing

The second is the pattern to build on: fire it from a webhook listener, an admin "refresh data" button, or a post-deploy warmup job. Cache aggressively (long TTL) and let events handle freshness — that is the whole value proposition.

// Supplier price change webhook
PriceChanged::dispatch(fn () => (new GetProductRequest($sku))->clearCache());

Match store to deployment shape. file store is fine for a single local process and useless the moment you run two PHP-FPM workers or Horizon consumers. Anything beyond local dev wants Redis (or your existing shared PSR-16 pool) so all workers see the same entries and clearCache() from a queued job actually invalidates what the web request will read.

TTL strategy by volatility. Short TTLs (30–120s) on semi-volatile data as a safety net; long TTLs (hours/days) on genuinely static reference data, paired with event-driven clearCache(). Never use TTL as your only freshness mechanism for data someone can edit.

Testing. Point resolveCacheDriver() at an in-memory/array store in tests and flush between cases, otherwise a cached response silently satisfies a MockClient assertion you expected to fail. Combine with Saloon's MockClient for the happy path and a separate test that asserts a second call does not hit the mock.

Migration leverage. If you're moving to Saloon v4, this plugin is the cheapest part of the migration — no custom caching wrapper to port. Do the plugin bump in the same PR as the Saloon bump so version constraints are validated together.


Gotchas and Tips

Cache keys ignore headers and auth by default. Two users hitting /me share a key. Two API versions distinguished only by an Accept header share a key. This is the highest-severity mistake you can make with this package — audit every CacheableRequest for "does the response vary on something not in the URL?" and put it in the key.

POST bodies collide on URL. Same endpoint, different payload, same key unless you include the body. If you cache anything non-GET, include the body hash explicitly.

No tag-based invalidation. clearCache() purges one key. There is no "flush everything under products:*". If you need group invalidation, either use a store with tag support behind your own driver, or version your key prefix (products:v7:...) and bump the version to nuke a namespace cheaply. Plan for this before you have 40 cacheable requests.

Interface and vendor names have moved across majors. The driver contract was renamed (CacheDriver → DriverInterface) and the vendor changed saloonhttp/cache-plugin → saloonphp/cache-plugin (v2.3.0 / v1.2.0). If a lockfile or a tutorial shows an old namespace, trust vendor/saloonphp/cache-plugin/src/Contracts over the docs. Also note v2 changed the TTL signature from a DateInterval (cacheFor()) to cacheExpiryInSeconds(): int — v1 examples do not port.

Debugging a cache hit looks like a broken request. No HTTP call means nothing in Telescope, no Guzzle log line, no API-side request ID. When a request "isn't running", first suspect the cache: call clearCache(), re-run, and see if the outbound call reappears. Temporarily wrap your driver in a decorator that Log::info()s every get/put if you need certainty.

Cached responses may skip request hooks. Anything you rely on in then() / response middleware / retry logic only runs when the response actually comes off the wire. If a side effect lives in a response handler, don't cache that request — or move the side effect out of the HTTP layer.

Rate limiting and caching interact confusingly. Cached responses don't consume the remote API's quota, so your limiter and your observed throughput will disagree under load. That's the win, but it makes capacity planning non-obvious — measure hit rate, not just latency.

Low adoption footprint (13 stars, zero known dependents). The API is small enough to read in an afternoon, so treat it as code you own: wrap the driver behind your own interface so you can swap in a hand-rolled implementation, and pin exact versions in composer.json rather than relying on a caret range. Upstream maintenance is real but thin — don't put it on the critical path without an escape hatch.

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.
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings
kda/laravel-package
kda/filament-dynamic-navigation
bypersoft/signbox
pairus/product-data
fayne/laravel-filament-cms-core
nodisrupt/laravel-agent
sierratecnologia/media-manager