cleaniquecoders/laravel-observers
A lightweight Laravel package for organizing and registering Eloquent model observers. Keep event logic tidy, reduce boilerplate, and connect model events to dedicated observer classes with a clean, maintainable setup.
Install the package with Composer; Laravel package discovery registers the service provider automatically:
composer require cleaniquecoders/laravel-observers
If the package ships publishable configuration, publish it first and review the defaults:
php artisan vendor:publish --tag="laravel-observers-config"
Look first at:
config/observers.php (if published) for namespace and auto-discovery settings.app/Observers and app/Models to confirm your existing observer naming matches the package convention.The fastest first use case is a single model lifecycle hook. Create an observer and let the package wire it up rather than editing AppServiceProvider::boot():
php artisan make:observer UserObserver --model=User
Then add the package trait or attribute to User as documented, or fall back to native registration if you prefer explicit wiring:
protected static function booted(): void
{
static::observe(UserObserver::class);
}
Version 4.4.0 is a compatibility release: Laravel 13, PHPUnit 12, and Pest 4 support, plus CI/dev-dependency updates. There are no advertised breaking changes, so upgrading from 4.3.x should be low risk. Confirm your own Laravel and PHP constraints before upgrading:
composer why-not cleaniquecoders/laravel-observers 4.4.0
Use observers for cross-cutting model side effects, not for domain logic that belongs in actions or services. Good candidates:
created, updated, deleted).creating).saved, deleted).created, updated).creating).Prefer convention-based registration for consistency. If the package discovers App\Models\User → App\Observers\UserObserver, keep that naming strict across the codebase. This removes repetitive Model::observe() calls and makes observer coverage predictable in code review.
Keep observers thin and injectable. Laravel resolves observer constructors from the container, so depend on services rather than calling facades directly:
final class UserObserver
{
public function __construct(private AuditLogger $logger) {}
public function created(User $user): void
{
$this->logger->record('user.created', $user);
}
}
Use lifecycle-specific methods deliberately. creating/updating are for mutating the model before persistence; created/updated/deleted are for side effects after persistence. Avoid performing slow external calls in creating or updating because they run inside the save path.
Guard against recursive writes. If an observer updates the same model, use updateQuietly() or saveQuietly() to avoid re-triggering events:
public function created(User $user): void
{
$user->updateQuietly(['reference' => Str::ulid()]);
}
Test observers at the boundary. Prefer asserting the observable outcome (audit row, cache key, dispatched job) rather than mocking the observer itself. When you need to isolate the model from observers, use Event::fake() or Model::withoutEvents(), but remember that this also suppresses unrelated model events.
Integration tip: if the package supports both auto-discovery and native #[ObservedBy], choose one registration mechanism per model. Mixing both is the most common cause of duplicate observer execution.
User::where(...)->update() and ->delete() do not fire Eloquent observer events. Use chunked model operations or explicit service-layer hooks when you need observer behavior at scale.saveQuietly(), updateQuietly(), deleteQuietly(), and Model::withoutEvents() are useful for recursion control but will silently skip your observer logic.Model::observe() call, or a duplicate #[ObservedBy] attribute. Observer duplication often appears as duplicate audit rows or double-dispatched jobs.php artisan config:clear locally and php artisan config:cache in deployment. Stale config can make discovery appear broken.illuminate constraints; it does not require Laravel 13. If you are on Laravel 11 or 12, the upgrade should be transparent. Run your test suite after upgrading because dev-dependency changes (PHPUnit 12, Pest 4) can surface assertion or lifecycle differences in your own tests.Event::listen('eloquent.*', fn ($event, $payload) => dump($event, $payload)) temporarily to confirm whether the issue is observer registration or observer logic. If the event fires but your observer does not, the problem is wiring; if the event does not fire, the problem is the Eloquent operation itself.How can I help you explore Laravel packages today?