NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages

Two layers sit in front of the database:

  • An in-request memo — always on. It lives for one request or one queued job: it’s bound scoped in the container and also dropped when a queue job starts or Octane receives a request, so a long-running worker never keeps serving a value another process has since changed.
  • A persistent cache — options.cache.enabled, on by default — in any Illuminate cache store. Writes update it and deletes drop the entry, so every process sees a change from its next request or job on.

A read checks the memo, then the persistent store, then the database (falling back to default()). set() writes the new raw value to both layers and forget() removes it from both — there is nothing else to invalidate.

Raw values, never casts

Both layers hold the raw stored string, never the cast value: an encrypted option stays ciphertext in the cache, and nothing but scalars is serialized, so Laravel’s unserialize hardening (cache.serializable_classes) can’t turn a cached Collection or Carbon into __PHP_Incomplete_Class. Each read casts the raw value.

OPTIONS_CACHE_ENABLED=true
OPTIONS_CACHE_STORE=redis   # null = the app’s default store
OPTIONS_CACHE_TTL=600       # seconds; null caches forever
OPTIONS_CACHE_PREFIX=options
OPTIONS_CACHE_TAG=options

Flushing

Options::flushCache(); // clears the in-request memo and the persistent entries
php artisan options:clear-cache

flushCache() and options:clear-cache work on every store. Each cache key embeds a generation token and a flush moves to a new one, so no old entry is read again — by other processes too, from their next request or job on. A taggable store (Redis, Memcached) also deletes the old entries at once; on any other store (file, database) they expire by cache.ttl, and with a null TTL they stay until the store evicts them.

Cache keys

Persistent keys follow {prefix}:{generation}:options:{key}:{scope}, where scope is global or an md5 of the length-prefixed owner morph class and owner key — so no two owners can share a key. The generation itself is stored under {prefix}:generation.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.