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’s EventDispatcher component lets application parts communicate by dispatching events to registered listeners and subscribers. Build decoupled, extensible workflows with a simple API for adding, removing, and prioritizing handlers.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps to Begin

  1. Installation:

    composer require symfony/event-dispatcher
    

    Laravel already includes this package as a dependency, so no additional installation is typically required for basic usage.

  2. First Use Case: Define an event class and dispatch it:

    // app/Events/CustomEvent.php
    namespace App\Events;
    use Illuminate\Queue\SerializesModels;
    use Illuminate\Foundation\Events\Dispatchable;
    
    class CustomEvent
    {
        use Dispatchable, SerializesModels;
    
        public function __construct(public string $message) {}
    }
    

    Dispatch the event:

    use App\Events\CustomEvent;
    
    event(new CustomEvent('Hello, EventDispatcher!'));
    
  3. Register a Listener: Create a listener class and register it in EventServiceProvider:

    // app/Listeners/CustomEventListener.php
    namespace App\Listeners;
    use App\Events\CustomEvent;
    
    class CustomEventListener
    {
        public function handle(CustomEvent $event)
        {
            info('Event received: ' . $event->message);
        }
    }
    

    Register the listener in EventServiceProvider:

    protected $listen = [
        CustomEvent::class => [
            CustomEventListener::class,
        ],
    ];
    
  4. Verify Integration: Run a test or check logs to confirm the listener is triggered when the event is dispatched.


Implementation Patterns

Core Workflows

1. Event-Driven Architecture

  • Pattern: Use events to decouple components. For example, an OrderPlaced event can trigger notifications, inventory updates, and analytics without direct service calls.
  • Example:
    // Dispatch event
    event(new OrderPlaced($order));
    
    // Listeners handle different concerns
    class SendOrderConfirmationEmail {
        public function handle(OrderPlaced $event) { /* ... */ }
    }
    
    class UpdateInventory {
        public function handle(OrderPlaced $event) { /* ... */ }
    }
    

2. Priority-Based Execution

  • Use priorities to control listener execution order. Higher priority listeners run first.
  • Example:
    protected $listen = [
        OrderPlaced::class => [
            ['App\Listeners\ValidateOrder', 'handle'], // Priority 0 (default)
            ['App\Listeners\LogOrder', 'handle'],     // Priority 10
            ['App\Listeners\SendNotification', 'handle'], // Priority -10
        ],
    ];
    

3. Subscriber Pattern

  • Group related listeners into a subscriber class for cleaner organization.
  • Example:
    // app/Events/OrderEventSubscriber.php
    namespace App\Events;
    use Symfony\Component\EventDispatcher\EventSubscriberInterface;
    
    class OrderEventSubscriber implements EventSubscriberInterface
    {
        public static function getSubscribedEvents()
        {
            return [
                OrderPlaced::class => 'onOrderPlaced',
                OrderCancelled::class => 'onOrderCancelled',
            ];
        }
    
        public function onOrderPlaced(OrderPlaced $event) { /* ... */ }
        public function onOrderCancelled(OrderCancelled $event) { /* ... */ }
    }
    

Register the subscriber in EventServiceProvider:

protected $subscribe = [
    OrderEventSubscriber::class,
];

4. Stopping Propagation

  • Use Event::until() or Event::halt() to stop further listeners from executing.
  • Example:
    public function handle(OrderPlaced $event)
    {
        if ($event->order->isInvalid()) {
            Event::halt($event); // Stops further listeners
        }
    }
    

5. Dynamic Listeners

  • Register listeners dynamically at runtime (e.g., based on user roles or configurations).
  • Example:
    $dispatcher = app(\Symfony\Component\EventDispatcher\EventDispatcherInterface::class);
    $dispatcher->addListener(OrderPlaced::class, function ($event) {
        // Dynamic logic
    });
    

Integration Tips

Laravel-Specific Tips

  • Leverage Laravel’s Event Facade:

    use Illuminate\Support\Facades\Event;
    
    Event::dispatch(new CustomEvent('Hello'));
    
  • Queue Listeners: Use Laravel’s queue system to process events asynchronously:

    protected $dispatchesAfter = [
        OrderPlaced::class => [
            SendOrderConfirmationEmail::class,
        ],
    ];
    
  • Event Service Provider: Centralize event-listener registration in EventServiceProvider for better maintainability.

Symfony-Specific Tips

  • Traceable Event Dispatcher: Use TraceableEventDispatcher for debugging event flows (note the v8.0.8 memory leak fix for long-running processes).

    $dispatcher = new \Symfony\Component\EventDispatcher\TraceableEventDispatcher($dispatcher);
    
  • Generic Event Objects: For Symfony-style generic events, use GenericEvent:

    use Symfony\Component\EventDispatcher\GenericEvent;
    
    $event = new GenericEvent($data, ['key' => 'value']);
    $dispatcher->dispatch($event, 'event.name');
    

Gotchas and Tips

Pitfalls

  1. Memory Leaks in Long-Running Processes:

    • Issue: The TraceableEventDispatcher had a memory leak in versions before v8.0.8. If using it in CLI workers, queues, or long-running scripts, ensure you’re on v8.0.8+.
    • Fix: Upgrade or avoid TraceableEventDispatcher in production long-running processes.
  2. Listener Ordering:

    • Issue: Listeners with the same priority may execute in an unpredictable order (e.g., due to PHP’s spl_object_hash).
    • Fix: Use explicit priorities or unique listener IDs:
      $dispatcher->addListener(OrderPlaced::class, [$listener, 'handle'], 10);
      
  3. Event Object Mutability:

    • Issue: Modifying event objects in listeners can lead to unexpected behavior if other listeners rely on the original state.
    • Fix: Clone event objects if mutation is needed:
      $eventClone = clone $event;
      
  4. Circular Dependencies:

    • Issue: Events dispatching other events can create circular dependencies, leading to infinite loops.
    • Fix: Use Event::until() or Event::halt() to break cycles or refactor logic.
  5. Performance Overhead:

    • Issue: Excessive listeners or complex event objects can slow down event dispatching.
    • Fix: Profile with TraceableEventDispatcher and optimize critical paths.

Debugging Tips

  1. Enable Event Tracing: Wrap the dispatcher in TraceableEventDispatcher for debugging:

    $dispatcher = new \Symfony\Component\EventDispatcher\TraceableEventDispatcher(app('events'));
    $dispatcher->dispatch(new CustomEvent('Debug'));
    
  2. Log Listener Execution: Add logging to listeners to trace execution:

    public function handle(CustomEvent $event)
    {
        \Log::debug('Listener executed for event: ' . $event->message);
    }
    
  3. Check for Unregistered Listeners: Use php artisan event:list to list all registered listeners and verify coverage.

  4. Test Event Propagation: Verify that Event::halt() or Event::until() works as expected in edge cases.


Extension Points

  1. Custom Event Dispatcher: Extend EventDispatcherInterface to add custom logic (e.g., caching, retry mechanisms):

    class CustomEventDispatcher implements EventDispatcherInterface
    {
        private $decorated;
    
        public function __construct(EventDispatcherInterface $decorated)
        {
            $this->decorated = $decorated;
        }
    
        public function dispatch(object|string $event, string $eventName = null): object
        {
            // Custom logic (e.g., caching)
            return $this->decorated->dispatch($event, $eventName);
        }
    
        // Delegate other methods to the decorated dispatcher
    }
    
  2. Event Subscriber Decorators: Decorate subscribers to add cross-cutting concerns (e.g., logging, metrics):

    class LoggingSubscriberDecorator implements EventSubscriberInterface
    {
        private $decorated;
    
        public function __construct(EventSubscriberInterface $decorated)
        {
            $this->decorated = $decorated;
        }
    
        public static function getSubscribedEvents()
        {
            return $this->decorated::getSubscribedEvents();
        }
    
        public function __call($name, $arguments)
        {
            \Log::info("Calling subscriber method: {$name}");
            return $this->decorated->$name(...$arguments);
        }
    }
    
  3. Dynamic Event Namespaces: Use Symfony’s EventDispatcher with dynamic event namespaces for modular applications:

    $
    
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.
davejamesmiller/laravel-breadcrumbs
artisanry/parsedown
christhompsontldr/phpsdk
enqueue/dsn
bunny/bunny
enqueue/test
enqueue/null
enqueue/amqp-tools
milesj/emojibase
bower-asset/punycode
bower-asset/inputmask
bower-asset/jquery
bower-asset/yii2-pjax
laravel/nova
spatie/laravel-mailcoach
spatie/laravel-superseeder
laravel/liferaft
nst/json-test-suite
danielmiessler/sec-lists
jackalope/jackalope-transport