Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Eloquent Observable Laravel Package

stayallive/laravel-eloquent-observable

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require stayallive/laravel-eloquent-observable
    

    Publish the config (optional):

    php artisan vendor:publish --provider="Stayallive\EloquentObservable\ServiceProvider"
    
  2. 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.
            ];
        }
    }
    
  3. 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.


Implementation Patterns

Usage Patterns

  1. Just-in-Time Registration:

    • Listeners are registered only when the model is first accessed in a request, reducing startup overhead.
    • Ideal for models with observers that are rarely used (e.g., LegacyUser, AuditLog).
  2. Model-Scoped Logic:

    • Keep event logic co-located with the model, improving readability and maintainability.
    • Example:
      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);
                      }
                  },
              ];
          }
      }
      
  3. Conditional Listeners:

    • Dynamically enable/disable listeners based on environment or model state:
      public static function observable(): array
      {
          return app()->environment('local')
              ? [
                  'creating' => function ($model) {
                      Log::debug("User created: {$model->email}");
                  },
              ]
              : [];
      }
      
  4. Reusing Listeners:

    • Extract reusable logic into closures or methods:
      public static function observable(): array
      {
          return [
              'created' => [$this, 'logCreation'],
          ];
      }
      
      protected function logCreation($model)
      {
          // Reusable logic
      }
      

Workflows

  1. Migration from Observers:

    • Replace Observer classes with observable() in models.
    • Remove observer registration from AppServiceProvider or similar:
      // Before (in AppServiceProvider)
      protected $observers = [
          User::class => UserObserver::class,
      ];
      
      // After (in User model)
      public static function observable(): array { ... }
      
  2. Testing:

    • Mock the observable() method in tests to isolate logic:
      $model = new User();
      $model->observable = fn () => ['created' => fn ($m) => $m->fireEvent = true];
      
  3. Integration with Policies/Authorizers:

    • Use listeners to trigger authorization checks or policy updates:
      public static function observable(): array
      {
          return [
              'updated' => function ($model) {
                  Policy::resetCacheFor($model);
              },
          ];
      }
      

Gotchas and Tips

Pitfalls

  1. Performance Misconceptions:

    • While this reduces startup overhead, just-in-time registration adds a tiny runtime cost when the model is first used. Benchmark if critical for high-frequency models.
    • Avoid overusing for models accessed in every request (e.g., User in auth middleware).
  2. Closure Scope Issues:

    • Closures in 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(); },
      
  3. Event Ordering:

    • Just-in-time listeners run after Laravel’s default observers (if any). Use Observer for critical pre-processing.
  4. Circular Dependencies:

    • Avoid recursive calls in listeners (e.g., triggering created from creating). Use flags or queues:
      'creating' => function ($model) {
          if (!$model->exists && !$model->wasRecentlyCreated) {
              // Logic here
          }
      },
      

Debugging

  1. Listener Not Firing:

    • Verify the model is being booted (e.g., via Model::booted() or Model::retrieved()).
    • Check for typos in event names (e.g., updating vs. updated).
  2. Config Overrides:

    • The package respects config('eloquent_observable.*'). Override defaults if needed:
      'events' => [
          'creating', 'created', 'updating', 'updated',
          'deleting', 'deleted', 'saving', 'saved', 'retrieved',
      ],
      
  3. Logging:

    • Enable debug logging to trace listener execution:
      'debug' => env('APP_DEBUG', false),
      

Extension Points

  1. Custom Events:

    • Extend the package to support custom events by modifying the observable() return type or using a trait:
      public static function observable(): array
      {
          return [
              'custom.event' => function ($model) { ... },
          ];
      }
      
  2. Dynamic Registration:

    • Register listeners dynamically based on runtime conditions:
      public static function observable(): array
      {
          return request()->has('debug')
              ? ['created' => fn ($model) => Log::debug($model)]
              : [];
      }
      
  3. Integration with Laravel 10+ Features:

    • Combine with Model::firstWhere() or Model::withoutEvents() for granular control:
      User::withoutEvents(function () use ($user) {
          $user->observable = []; // Temporarily disable listeners
          $user->save();
      });
      
  4. Testing Utilities:

    • Add helper methods to test listeners:
      // In a test trait
      public function assertListenerFired($model, $event)
      {
          $model->fireModelEvent($event);
          // Assert side effects
      }
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
hamzi/corewatch
minionfactory/raw-hydrator
hexters/coinpayment
rjcodes/rjcms
act-training/laravel-permissions-manager
alimarchal/laravel-chart-of-accounts
babenkoivan/elastic-scout-driver
mkwebdesign/filament-watchdog-v5
renatomarinho/laravel-page-speed
zedmagdy/filament-business-hours
renatovdemoura/blade-elements-ui
devgeek/beacon-admin
benjamin-rqt/data-watcher-bundle
atriumphp/atrium
sandermuller/package-boost-laravel
sandermuller/boost-skills
redaxo/core
yusufgenc/filament-api-forge
l3aro/rating-star-for-filament
leek/filament-subtenant-scope