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

Observers react to one specific option changing, without a global listener that filters by key. Register them in a service provider’s boot():

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Cache;
use RoundlyConsulting\Options\DataTransferObjects\OptionChange;
use RoundlyConsulting\Options\Enums\OptionChangeType;
use RoundlyConsulting\Options\Facades\Options;

Options::observe(ThemeOption::class, function (mixed $value, ?Model $owner, OptionChange $change): void {
    // $change->type is OptionChangeType::Set or ::Forgotten
    Cache::forget('compiled-theme');
});

// Invokable class-string (resolved from the container), registered by key:
Options::observe('theme', RecompileThemeListener::class);

Options::forgetObservers(ThemeOption::class); // drop one option’s observers
Options::flushObservers();                    // drop them all

Invokable observers

A class-string observer is resolved from the container on each change, so constructor injection works:

use Illuminate\Database\Eloquent\Model;
use RoundlyConsulting\Options\DataTransferObjects\OptionChange;

final class RecompileThemeListener
{
    public function __construct(private ThemeCompiler $compiler) {}

    public function __invoke(mixed $value, ?Model $owner, OptionChange $change): void
    {
        $this->compiler->compileFor($owner);
    }
}

A class-string without __invoke throws InvalidOptionObserver when you register it.

The change payload

Every observer receives (mixed $value, ?Model $owner, OptionChange $change):

PropertyTypeMeaning
keystringThe option’s key().
optionClassclass-stringThe option class that changed.
typeOptionChangeTypeOptionChangeType::Set or OptionChangeType::Forgotten.
valuemixedThe new value, cast — the same value get() returns. null for a forgotten option.
owner?ModelThe scope that changed — null for the global scope.

Rules

  • Observers fire on set() and forget(), in registration order — never on reads.
  • Registering by class-string or by registry key targets the same option.
  • They ride OptionSet and OptionForgotten, so they need options.events.enabled — except under Options::fake(), which fires them directly.

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.