envant/fireable
Envant Fireable is a Laravel package that automatically fires events when chosen Eloquent model attributes change. Define fireable fields on a model and react to updates with dedicated events, avoiding manual observer boilerplate and keeping model change handling clean and explicit.
Install with Composer and let the package's trait do the wiring:
composer require envant/fireable
The first place to look is the package's Fireable trait and its $fireable property contract — that map is the whole API surface. In a model, add the trait and declare which attributes should dispatch which event classes:
use Envant\Fireable\Fireable;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
use Fireable;
protected $fireable = [
'email' => \App\Events\UserEmailChanged::class,
'name' => \App\Events\UserNameChanged::class,
];
}
Create ordinary Laravel event classes (they receive the model in the constructor). Then save the model:
$user->email = '[email protected]';
$user->save(); // UserEmailChanged is dispatched
The canonical first use case is an audit trail or notification fan-out: you want "when this column changes, do X" without writing a full observer for every attribute. Start there — one mapped attribute, one listener that writes an audit row — before rolling it out across models.
Attribute-to-event map as the source of truth. Keep the $fireable array adjacent to $fillable/$casts so reviewers can see the model's side effects at a glance. This is the package's main value over hand-written observers: the trigger conditions are declarative and grep-able.
Many attributes → one event. Point several keys at the same event class when a single "profile changed" event makes more sense than per-field events, and inspect $model->getChanges() (or wasChanged('email')) inside the listener to branch. This keeps your event count small and pushes decision logic into listeners where it's testable.
Pair with queued listeners. Keep the model save fast; let listeners implement ShouldQueue for emails, webhooks, or third-party syncs. If you're on Laravel 8+, implement ShouldQueueAfterCommit (or set $afterCommit = true) so listeners don't run against uncommitted data.
Compose with observers, don't replace them. Use Fireable for the "attribute changed" cases and keep observers for lifecycle concerns (created, deleted, restored). If you find yourself mapping 15 attributes, an observer with isDirty() checks is probably clearer.
Testing. Use Event::fake([UserEmailChanged::class]) and Event::assertDispatched() around a save(). Because the package only reacts to dirty attributes, tests should explicitly mutate then save — a factory()->create() will fire every mapped event (see Gotchas), which can silently break unrelated tests.
Integration tip: centralize the "what changed" payload by making your events expose $model and a computed diff, rather than passing raw values. Listeners stay stable if the model gains columns later.
create(), all set attributes are dirty, so every mapped event dispatches. If you only care about updates, guard inside listeners (e.g. if ($model->wasRecentlyCreated) return;) — the package has no built-in "skip on create" flag.save() without mutating first.User::where(...)->update([...]) never hydrates models and never fires model events, so Fireable won't trigger. Same for insert(), upsert(), and raw SQL. Document this for anyone who assumes the events are database-level.saveQuietly() suppresses it. Since the trait rides the model's saved event, saveQuietly() and withoutEvents() silence Fireable too — useful as an escape hatch inside listeners to avoid recursion.saveQuietly() or a re-entrancy flag, otherwise you get an infinite loop that only shows up in staging.$model->getDirty() before saving — that's your first debugging step.save(), inside any open transaction. Without afterCommit on listeners, a rollback can leave you with dispatched emails for data that never persisted.ShouldQueueAfterCommit behavior, which is a listener concern rather than the package's.BaseModel to add conventions like auto-prefixing event namespaces — cheaper than forking the package.How can I help you explore Laravel packages today?