stayallive/laravel-eloquent-observable
Installation:
composer require stayallive/laravel-eloquent-observable
Publish the config (optional):
php artisan vendor:publish --provider="Stayallive\EloquentObservable\ServiceProvider"
Basic Usage: Define event listeners directly in your Eloquent model:
use Stayallive\EloquentObservable\Traits\Observable;
class User extends Model
{
use Observable;
public static function observable(): array
{
return [
'creating' => function ($model) {
// Logic for creating event
},
'created' => function ($model) {
// Logic for created event
},
// Add other events: updating, updated, deleting, deleted, saving, saved, etc.
];
}
}
First Use Case:
Replace a traditional observer for a specific model (e.g., UserObserver) with the observable() method. This avoids global observer registration and reduces boot time overhead.
Just-in-Time Registration:
LegacyUser, AuditLog).Model-Scoped Logic:
class Post extends Model
{
use Observable;
public static function observable(): array
{
return [
'saving' => function ($model) {
if (empty($model->slug)) {
$model->slug = Str::slug($model->title);
}
},
];
}
}
Conditional Listeners:
public static function observable(): array
{
return app()->environment('local')
? [
'creating' => function ($model) {
Log::debug("User created: {$model->email}");
},
]
: [];
}
Reusing Listeners:
public static function observable(): array
{
return [
'created' => [$this, 'logCreation'],
];
}
protected function logCreation($model)
{
// Reusable logic
}
Migration from Observers:
Observer classes with observable() in models.AppServiceProvider or similar:
// Before (in AppServiceProvider)
protected $observers = [
User::class => UserObserver::class,
];
// After (in User model)
public static function observable(): array { ... }
Testing:
observable() method in tests to isolate logic:
$model = new User();
$model->observable = fn () => ['created' => fn ($m) => $m->fireEvent = true];
Integration with Policies/Authorizers:
public static function observable(): array
{
return [
'updated' => function ($model) {
Policy::resetCacheFor($model);
},
];
}
Performance Misconceptions:
User in auth middleware).Closure Scope Issues:
observable() may not have access to $this or other model properties. Use explicit parameters:
// Bad: Assumes $this is available
'created' => function () { $this->doSomething(); },
// Good: Explicit model parameter
'created' => function ($model) { $model->doSomething(); },
Event Ordering:
Observer for critical pre-processing.Circular Dependencies:
created from creating). Use flags or queues:
'creating' => function ($model) {
if (!$model->exists && !$model->wasRecentlyCreated) {
// Logic here
}
},
Listener Not Firing:
Model::booted() or Model::retrieved()).updating vs. updated).Config Overrides:
config('eloquent_observable.*'). Override defaults if needed:
'events' => [
'creating', 'created', 'updating', 'updated',
'deleting', 'deleted', 'saving', 'saved', 'retrieved',
],
Logging:
'debug' => env('APP_DEBUG', false),
Custom Events:
observable() return type or using a trait:
public static function observable(): array
{
return [
'custom.event' => function ($model) { ... },
];
}
Dynamic Registration:
public static function observable(): array
{
return request()->has('debug')
? ['created' => fn ($model) => Log::debug($model)]
: [];
}
Integration with Laravel 10+ Features:
Model::firstWhere() or Model::withoutEvents() for granular control:
User::withoutEvents(function () use ($user) {
$user->observable = []; // Temporarily disable listeners
$user->save();
});
Testing Utilities:
// In a test trait
public function assertListenerFired($model, $event)
{
$model->fireModelEvent($event);
// Assert side effects
}
How can I help you explore Laravel packages today?