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 Options Laravel Package

overtrue/laravel-options

A simple key-value options manager for Laravel. Store application settings in the database, cache them for fast retrieval, and access values through helpers or the Options facade. Supports defaults, forgetting, and bulk updates.

View on GitHub
Deep Wiki
Context7

Getting Started

Requirements for 4.x: PHP 8.3+ and Laravel 13.x. If you are on Laravel 9, 10, 11 (or 12), do not upgrade — pin ^3.0 and stay on the 3.x line. Composer will refuse the install anyway, but pin explicitly so a composer update doesn't surprise you.

composer require overtrue/laravel-options

php artisan vendor:publish --tag=options-config   # config/options.php
php artisan vendor:publish --tag=options-migrations
php artisan migrate

Where to look first, in order:

  1. config/options.php — this is the whole mental model of the package. It tells you which provider backs your options (database table, cache, array), and it's the file you'll edit when you want a different backing store per environment.
  2. The published migration — the persistence schema. 4.0.0 explicitly preserves this schema, so anything you built against the table in 3.x still works.
  3. Overtrue\LaravelOptions\Option (the facade root) — the public surface is small: get, set, has, remove, all, plus provider().

First use case — site settings:

use Overtrue\LaravelOptions\Option;

Option::set('site.name', 'Acme');
Option::set('site.theme', ['primary' => '#0f172a', 'dark' => true]);

Option::get('site.name');              // 'Acme'
Option::get('missing', 'fallback');   // 'fallback'
Option::has('site.theme');            // true
Option::remove('site.theme');         // bool

That's 90% of real-world usage. Everything else in the package is about where those key/values live and how to move them around.

Implementation Patterns

Read with a default, always. Option::get($key, $default) is the pattern that keeps settings screens from exploding on a fresh install. Wrap it in your own typed accessor so the rest of the app never touches raw strings:

class Settings
{
    public static function bool(string $key, bool $default = false): bool
    {
        return filter_var(Option::get($key, $default), FILTER_VALIDATE_BOOLEAN);
    }
}

Structured values are first-class. Arrays/objects round-trip through the provider (the "JSON updates" integration coverage in 4.0.0 confirms this path is exercised). Store config-shaped data as an array and read the whole thing back rather than trying to encode nested structures into dotted string keys — you'll fight your own convention otherwise.

Swap providers by context, not by code. Option::provider($name) resolves a provider instance, and provider() with no argument returns the configured default. That gives you three clean uses:

  • Testing: point the config at the array/in-memory provider in phpunit.xml so option writes don't leak between tests and you skip RefreshDatabase entirely for unit tests.
  • Multi-tenant: resolve a provider per tenant (prefix the key or bind a tenant-aware provider) rather than prefixing 400 call sites.
  • Custom providers: implement the provider contract and register it in config/options.php, then instantiate via Option::provider('my_provider'). This is the documented extension point and 4.0.0 added integration coverage for provider reuse and custom providers — so it's a supported path, not a hack.

Console command for ops. The bundled Artisan command is the right way to seed options in deploy scripts or inspect values on a box where you don't have Tinker. Run php artisan list to see the exact signature in your installed version; wire it into your deploy step for idempotent defaults.

Treat the facade as a repository, not a config store. Laravel's own config() is cached and immutable at runtime; options are the opposite — mutable, persisted, per-row. Use options for values an admin edits at runtime, and keep deployment-time values in config/. Mixing them is the most common architectural mistake with this package.

Upgrading from 3.x: read UPGRADE.md first. Because the schema and provider/event APIs are preserved in 4.0.0, the upgrade is mostly a version-constraint exercise — but the PHP 8.3/Laravel 13 floor means it's a platform upgrade, not a package upgrade. Do it as its own commit.

Gotchas and Tips

Version floor is the headline breaking change. 4.0.0 requires PHP 8.3+ and Laravel 13.x. There is no gradual path: applications on Laravel 9–11 must remain on 3.x. If you maintain a package that depends on this one, widen your constraint to ^3.0|^4.0 rather than bumping to ^4.0, or you've silently dropped every Laravel 11 app.

The two fixed TypeErrors are worth understanding — they tell you where the sharp edges were:

  • No-argument Option::get() previously threw a TypeError in the documented read path. If you had defensive try/catch or a null-coalescing wrapper around a bare get(), you can remove it — but note that the return contract of a no-argument read is easy to misread. Be explicit about keys in application code rather than relying on the whole-collection read; Option::all() is the clearer intent.
  • Option::get('0') — the string "0" is falsy in PHP, and this tripped the internal key handling. It's fixed, but the class of bug is still yours to avoid: don't gate option reads on if ($value) or ?:. Use Option::has($key) to test existence and !== null to test for a stored null. Keys like "0", "false", and "" are all legitimate and all falsy.

provider(?string $name = null) is now explicitly nullable. This clears a PHP 8.4 deprecation without changing accepted inputs — but if you extend the Option class or a provider and override that method, match the signature exactly. An implicit-nullable override in your own code will emit the same deprecation on 8.4+ and, worse, can trip a fatal signature mismatch.

Null vs. missing vs. false. get($key) on an absent key and get($key) on a key whose stored value is null are indistinguishable from the caller's side. If "unset" and "explicitly empty" mean different things in your domain, store a sentinel or check has() first.

Cache-backed providers and invalidation. If your configured provider sits in front of a cache, writes need to bust the right keys. When a setting "doesn't update," confirm which provider is actually resolved (Option::provider() with no args) before debugging your controller — in a multi-provider setup it's very easy to write through one provider and read through another.

Dotted keys are just strings here. Don't assume Laravel's Arr::get-style dot traversal applies to stored keys. site.name is likely a literal key, not a nested lookup. Pick one convention in the codebase (flat dotted strings are fine) and enforce it, or you'll get silent misses rather than errors.

Testing tip: since options are global singleton-ish state, reset them in setUp() when using the array provider, and prefer asserting through your own Settings wrapper so the tests fail for domain reasons rather than persistence-plumbing reasons. 4.0.0's regression tests cover exactly the edge cases (selected keys, JSON updates, null/default behavior, deletion) worth mirroring in your own suite.

Extension points that survived the major bump: the persistence schema and the provider/event APIs are unchanged. Any custom provider or event listener you wrote for 3.x should port to 4.x without modification — which also means a provider bug from 3.x will follow you across, so re-run your provider tests after the upgrade.

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.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels