Table of Contents
When dispatching events in Laravel services, inject the Dispatcher instead of using the event() helper function. This makes your code more testable and explicit about its dependencies.
The Problem with event()
Most Laravel code uses the event() helper to dispatch events:
class OrderProcessor
{
public function process($order)
{
// ... processing logic ...
event(new OrderCompleted($order));
}
}
This works fine, but it makes testing harder. You can’t easily mock the event system, and it’s not immediately clear that this class has a dependency on Laravel’s event system.
The Solution: Dependency Injection
Instead, inject the Illuminate\Events\Dispatcher:
use Illuminate\Events\Dispatcher;
class OrderProcessor
{
public function __construct(
private Dispatcher $dispatcher
) {}
public function process($order)
{
// ... processing logic ...
$this->dispatcher->dispatch(
new OrderCompleted($order)
);
}
}
Why This Matters
Testing becomes trivial:
public function test_processing_dispatches_event()
{
$dispatcher = Mockery::mock(Dispatcher::class);
$dispatcher->shouldReceive('dispatch')
->once()
->with(Mockery::type(OrderCompleted::class));
$processor = new OrderProcessor($dispatcher);
$processor->process($order);
}
Dependencies are explicit: Anyone reading the constructor knows immediately that this class dispatches events. No hidden global dependencies.
Framework agnostic: You could swap the event system by implementing the same interface. The event() helper ties you to Laravel’s globals.
When to Use Which
Use event() in:
- Controllers (already framework-coupled)
- Blade views (convenience matters more)
- Quick scripts and seeders
Use injected Dispatcher in:
- Services that need testing
- Domain logic classes
- Anywhere you want explicit dependencies
The pattern extends to other Laravel facades too: inject Illuminate\Contracts\Cache\Repository instead of using Cache::get(), inject Illuminate\Contracts\Queue\Queue instead of Queue::push(), and so on.
Your tests will thank you.
Leave a Reply