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

Event Dispatcher Laravel Package

symfony/event-dispatcher

Symfony EventDispatcher component lets application parts communicate via events. Dispatch events and register listeners or subscribers to react to them, enabling decoupled, extensible architectures with a lightweight, reusable event system.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require symfony/event-dispatcher
    

    Laravel already includes this package as a dependency, so no additional setup is required unless you're using Symfony-specific features.

  2. First Use Case: Dispatch an event and listen to it:

    // Dispatch an event
    $dispatcher = app(\Symfony\Component\EventDispatcher\EventDispatcher::class);
    $event = new \Symfony\Component\EventDispatcher\GenericEvent('user.created', ['user_id' => 1]);
    $dispatcher->dispatch($event, 'user.created');
    
    // Register a listener
    $dispatcher->addListener('user.created', function ($event) {
        // Handle the event
    });
    
  3. Laravel Integration: Use Laravel’s built-in Event facade for seamless integration:

    use Illuminate\Support\Facades\Event;
    
    // Dispatch
    Event::dispatch(new UserCreated(1));
    
    // Listen (in EventServiceProvider)
    protected $listen = [
        UserCreated::class => [
            'App\Listeners\SendWelcomeEmail',
        ],
    ];
    

Where to Look First

  • Symfony Documentation for core concepts.
  • Laravel’s Event facade for Laravel-specific usage.
  • #[AsEventListener] attribute (Symfony 7.4+) for modern PHP attribute-based listeners.

Implementation Patterns

Core Workflows

  1. Event-Driven Architecture: Replace direct service calls with events to decouple components.

    // Before (tight coupling)
    $authService->verify($user);
    
    // After (decoupled)
    event(new UserVerificationRequested($user));
    
  2. Listener Registration:

    • Static Registration (Laravel):
      // app/Providers/EventServiceProvider.php
      protected $listen = [
          UserVerificationRequested::class => [
              'App\Listeners\LogVerification',
              'App\Listeners\SendVerificationEmail',
          ],
      ];
      
    • Dynamic Registration (Symfony):
      $dispatcher->addListener(UserVerificationRequested::class, $listener);
      
  3. Event Propagation: Control whether subsequent listeners execute:

    $event->stopPropagation(); // Stops further listeners
    
  4. Priority-Based Listeners: Use priorities to define execution order:

    $dispatcher->addListener(UserVerificationRequested::class, [$listener, 'handle'], 100);
    
  5. Subscribers: Group related listeners in a single class:

    class UserVerificationSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            return [
                UserVerificationRequested::class => 'handleVerification',
            ];
        }
    
        public function handleVerification(UserVerificationRequested $event)
        {
            // Logic
        }
    }
    

Integration Tips

  • Laravel Queues: Dispatch events in jobs to trigger async listeners:

    class ProcessUserDataJob implements ShouldQueue
    {
        public function handle()
        {
            event(new UserDataProcessed($userId));
        }
    }
    
  • Real-Time Updates: Use Laravel Echo or Pusher to broadcast events:

    class OrderStatusUpdatedListener
    {
        public function handle(OrderStatusUpdated $event)
        {
            broadcast(new OrderStatusUpdatedBroadcast($event->orderId, $event->status));
        }
    }
    
  • Event Sourcing: Store events in a database for audit trails:

    class StoreEventListener
    {
        public function handle(GenericEvent $event)
        {
            EventStore::store($event);
        }
    }
    
  • Dynamic Feature Flags: Toggle listeners based on config:

    if (config('features.new_ui')) {
        $dispatcher->addListener(UserCreated::class, $newUIListener);
    }
    

Gotchas and Tips

Pitfalls

  1. Memory Leaks in TraceableEventDispatcher:

    • Issue: Long-running processes (e.g., CLI scripts, queues) may leak memory if TraceableEventDispatcher is reset during dispatch.
    • Fix: Upgrade to v8.0.9+ or avoid resetting the dispatcher mid-dispatch.
    • Workaround: Use a simple EventDispatcher instead for non-debugging scenarios.
  2. Listener Order Chaos:

    • Issue: Unpredictable listener execution order if priorities aren’t set.
    • Fix: Always define priorities explicitly:
      $dispatcher->addListener('event.name', [$listener, 'method'], 100);
      
  3. Circular Dependencies:

    • Issue: Listeners dispatching events that trigger other listeners can create infinite loops.
    • Fix: Use stopPropagation() or guard against loops:
      if (!$event->isPropagationStopped()) {
          $event->stopPropagation();
      }
      
  4. Performance Overhead:

    • Issue: Excessive listeners or complex event objects can slow down dispatch.
    • Fix: Benchmark critical paths and optimize event payloads.
  5. Laravel-Specific Quirks:

    • Issue: Laravel’s Event facade uses a singleton dispatcher, which may not behave identically to Symfony’s EventDispatcher.
    • Fix: Use app(EventDispatcher::class) for Symfony-specific features.

Debugging Tips

  1. Enable Debugging: Use TraceableEventDispatcher to log dispatched events:

    $dispatcher = new TraceableEventDispatcher();
    $dispatcher->addListener('*', function ($event) {
        \Log::debug('Event dispatched:', ['event' => $event->getName()]);
    });
    
  2. Listener Not Firing:

    • Verify the listener is registered (check EventServiceProvider or dynamic additions).
    • Ensure the event class matches the listener’s expected type (use #[AsEventListener] for type safety).
  3. Event Not Stopping Propagation:

    • Confirm stopPropagation() is called on the event object.
    • Check for typos in event names or listener methods.

Extension Points

  1. Custom EventDispatcher: Extend EventDispatcher for custom logic:

    class CustomEventDispatcher extends EventDispatcher
    {
        public function dispatchWithMiddleware($event, $eventName = null)
        {
            // Add middleware logic
            parent::dispatch($event, $eventName);
        }
    }
    
  2. Event Subscriber Interface: Implement EventSubscriberInterface for reusable subscriber logic:

    class AnalyticsSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            return [
                'user.created' => 'trackUserCreation',
                'order.placed' => 'trackOrder',
            ];
        }
    }
    
  3. Event Attributes (PHP 8.0+): Use #[AsEventListener] for modern attribute-based listeners:

    #[AsEventListener(event: UserCreated::class, method: 'handle')]
    class UserCreatedListener
    {
        public function handle(UserCreated $event)
        {
            // Logic
        }
    }
    
  4. Laravel Service Provider Integration: Bind Symfony’s EventDispatcher to Laravel’s container:

    public function register()
    {
        $this->app->singleton(\Symfony\Component\EventDispatcher\EventDispatcher::class, function ($app) {
            $dispatcher = new EventDispatcher();
            // Add listeners dynamically
            return $dispatcher;
        });
    }
    

Configuration Quirks

  1. Laravel’s Event Dispatcher: Laravel’s Event facade uses a singleton instance of Illuminate\Events\Dispatcher, which is a wrapper around Symfony’s EventDispatcher. To access the underlying Symfony dispatcher:

    $symfonyDispatcher = app(\Symfony\Component\EventDispatcher\EventDispatcher::class);
    
  2. Priority Defaults: Symfony’s default priority is 0. Laravel’s EventServiceProvider may override this, so explicitly set priorities if needed.

  3. Global vs. Local Listeners:

    • Global listeners (registered in EventServiceProvider) run for all dispatches.
    • Local listeners (added dynamically) can be scoped to specific contexts (e.g., HTTP requests).
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.
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
anil/file-picker
broqit/fields-ai