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

Defining options

Every option is a small class extending BaseOption. With no overrides, the key is the class basename, the value casts to string and the default is null:

use RoundlyConsulting\Options\BaseOption;

final class SimpleOption extends BaseOption
{
    // Uses all defaults: key 'SimpleOption', cast 'string', default null.
}

Override the hooks to customise it — key(), default(), castAs(), encrypted(), rules(), authorizeRead() / authorizeWrite() and the presentation methods label(), help(), section() and order():

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use RoundlyConsulting\Options\BaseOption;

final class NotificationPreferences extends BaseOption
{
    public function key(): string
    {
        return 'notification-preferences';
    }

    public function default(): mixed
    {
        return collect(['email' => true, 'sms' => false]);
    }

    public function castAs(): string|CastsAttributes
    {
        return 'collection';
    }
}

The examples throughout these docs use a ThemeOption like this one:

namespace App\Options;

use Illuminate\Contracts\Database\Eloquent\CastsAttributes;
use RoundlyConsulting\Options\BaseOption;

final class ThemeOption extends BaseOption
{
    public function key(): string
    {
        return 'theme';
    }

    public function default(): mixed
    {
        return 'light';
    }

    public function castAs(): string|CastsAttributes
    {
        return 'string';
    }
}

What you can override

MethodDefaultPurpose
key(): stringclass basenameThe storage key — unique per option.
default(): mixednullReturned when nothing is stored for the scope.
castAs(): string|CastsAttributes'string'How the stored value is cast on read and write.
encrypted(): boolfalseEncrypt the value at rest.
rules(): array|string[]Laravel validation rules applied on set().
readable(): stringtitle-cased keyHuman name — notification-preferences becomes Notification Preferences. Used by options:list.
label(): stringreadable()Field label in a group definition.
help(): ?stringnullHelp text under the field.
section(): ?stringnullSection or tab a UI may group the field under.
order(): int0Sort order within a group (ascending).
authorizeRead(?Authenticatable, ?Model): booltrueRead hook, enforced when authorization is enabled.
authorizeWrite(?Authenticatable, ?Model): booltrueWrite hook, enforced when authorization is enabled.

label(), help(), section() and order() are documented by the optional HasPresentation contract, authorizeRead() and authorizeWrite() by AuthorizesOptions. BaseOption already implements both sets, so implementing the contracts is never required.

Generating options

make:option scaffolds a class in app/Options. --key and --cast fill in the key and cast, --encrypted turns encryption on, and --enum switches to a stub that casts through EnumCast:

php artisan make:option ThemeOption --cast=string --key=theme
php artisan make:option SecretToken --encrypted
php artisan make:option StatusOption --enum="App\Enums\Status"

Using an option class directly

Each option also works on its own — make() for the global scope, for() for an owner. This is shorthand, not a separate path: value(), set(), has(), forget(), reset() and remember() are final and run through the same OptionsManager as the facade, so they behave identically and Options::fake() sees them:

ThemeOption::make()->value();          // global value, or default() when unset
ThemeOption::make()->set('dark');      // global write

ThemeOption::for($user)->value();      // owner-scoped read
ThemeOption::for($user)->set('light'); // owner-scoped write
ThemeOption::for($user)->has();
ThemeOption::for($user)->forget();

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.