alexstewartja/laravel-attribute-observer
Laravel Attribute Observer lets you react to changes on specific Eloquent model attributes. Define observers for individual fields instead of the whole model, keeping update logic clean, focused, and easy to register in your service provider.
composer require alexstewartja/laravel-attribute-observer. The package is auto-discovered; there is usually no service provider to register manually.illuminate/* constraints allow Laravel 13 before upgrading.email changes, regenerate a slug only when title changes, or invalidate a cache only when status changes — instead of writing if ($model->wasChanged('email')) inside a broad updated observer.Minimal shape:
use Alexstewartja\AttributeObserver\Concerns\HasAttributeObservers;
class User extends Model
{
use HasAttributeObservers;
protected array $attributeObservers = [
'email' => \App\Observers\EmailAttributeObserver::class,
];
}
namespace App\Observers;
use Alexstewartja\AttributeObserver\AttributeObserver;
class EmailAttributeObserver extends AttributeObserver
{
public function updated($model, string $attribute, mixed $old, mixed $new): void
{
// Only runs when the observed attribute changes.
$model->sendEmailVerificationNotification();
}
}
Verify the exact observer method signatures and registration property against the installed version's README; the package's public API is small, so this should take only a few minutes.
wasChanged() checks.role, status, email, price). Capture old/new values directly from the observer arguments where available, or fall back to getOriginal() / getChanges().->afterCommit() on the job or DB::afterCommit() so external systems never see uncommitted state.$model->isDirty($attribute) before save and $model->wasChanged($attribute) after save.Event::fake(), Queue::fake(), and Notification::fake() around model updates, then assert the expected job/notification was dispatched only when the observed attribute actually changed. Add negative tests where another attribute changes and the observer must not fire.saveQuietly(), updateQuietly(), withoutEvents(), and bulk query updates (Model::where(...)->update(...)) will not fire model observers. If a critical integration must run for bulk changes, trigger it explicitly or use a database-level mechanism.withoutEvents(), a static flag, or by changing a different attribute and checking isDirty() carefully.isDirty() / getDirty(); after save, use wasChanged() / getChanges(). Casts matter: an attribute may appear unchanged at the raw level but differ after casting, or vice versa.afterCommit.How can I help you explore Laravel packages today?