cakephp/event
Lightweight event dispatcher for CakePHP apps. Define and fire events, attach listeners/subscribers, and manage propagation and results. Useful for decoupling components and building extensible plugins with a simple, familiar API.
Installation Add the package via Composer (though this is a CakePHP split, Laravel users can still leverage its core event dispatching logic):
composer require cakephp/event
Note: Since this is a CakePHP package, you’ll need to manually include its autoloader or use a facade-like wrapper in Laravel.
Basic Setup
Initialize the event manager in a Laravel service provider (e.g., AppServiceProvider):
use Cake\Event\EventManager;
public function register()
{
$this->app->singleton('eventManager', function ($app) {
return new EventManager();
});
}
First Use Case: Listening to an Event
Define a listener (e.g., app/Listeners/UserRegisteredListener.php):
use Cake\Event\EventListenerInterface;
class UserRegisteredListener implements EventListenerInterface
{
public function implementedEvents()
{
return [
'User.registered' => 'handleUserRegistered'
];
}
public function handleUserRegistered($event)
{
// Handle logic (e.g., log, notify, etc.)
Log::info('User registered: ' . $event->data['user_id']);
}
}
Attach the listener to the event manager:
$eventManager = app('eventManager');
$eventManager->on('User.registered', [$listener, 'handleUserRegistered']);
Triggering an Event Dispatch an event from anywhere in your app:
$event = new \Cake\Event\Event('User.registered', $this, [
'user_id' => 123,
'email' => 'user@example.com'
]);
app('eventManager')->dispatch($event);
Decoupled Event-Driven Logic
Use events to separate concerns (e.g., notifications, analytics, or background jobs) from core business logic.
Example: Trigger Order.placed to dispatch emails, update inventory, and log analytics—all without coupling these actions to the order creation code.
Middleware-Like Event Handling Chain listeners for sequential processing:
$eventManager->on('Order.processed', [$listener1, 'validate']);
$eventManager->on('Order.processed', [$listener2, 'ship']);
$eventManager->on('Order.processed', [$listener3, 'notify']);
Dynamic Event Binding Bind listeners conditionally (e.g., based on user roles or config):
if (config('app.feature_flags.notifications')) {
$eventManager->on('User.created', [$notifier, 'sendWelcomeEmail']);
}
Event Subscribers
Group related listeners in a subscriber class (similar to Laravel’s HandleEvents):
class UserEventSubscriber implements EventListenerInterface
{
public function implementedEvents()
{
return [
'User.created' => 'onUserCreated',
'User.updated' => 'onUserUpdated',
'User.deleted' => 'onUserDeleted',
];
}
}
Laravel Facade Wrapper Create a facade to simplify usage:
// app/Facades/EventManager.php
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class EventManager extends Facade
{
protected static function getFacadeAccessor() { return 'eventManager'; }
}
Usage:
EventManager::dispatch(new \Cake\Event\Event('User.registered', $this, [...]));
Laravel Events Bridge
Convert CakePHP events to Laravel’s Illuminate\Contracts\Events\Dispatcher for seamless integration with Laravel’s ecosystem (e.g., event() helper):
// In a service provider
$laravelDispatcher = app('events');
$cakeEventManager = app('eventManager');
$cakeEventManager->on('User.registered', function ($event) use ($laravelDispatcher) {
$laravelDispatcher->dispatch(new \App\Events\UserRegistered($event->data));
});
Priority-Based Listeners Use priorities to control listener execution order (higher numbers run first):
$eventManager->on('Order.processed', [$listener, 'log'], ['priority' => 10]);
Circular Dependencies
Avoid circular event loops (e.g., EventA triggers EventB, which triggers EventA again). Use a flag or counter to prevent infinite recursion:
if (!isset($event->data['_processed'])) {
$event->data['_processed'] = true;
// Process event...
}
Memory Leaks
Unbind listeners when no longer needed (e.g., in a controller’s __destruct or after a job completes):
$eventManager->off('User.created', [$listener, 'handle']);
Event Data Mutability CakePHP events pass data by reference. Avoid modifying the original data object unless intentional:
// Risky: Modifies the original $event->data
$event->data['status'] = 'updated';
// Safer: Clone or copy data
$data = $event->data;
$data['status'] = 'updated';
Namespace Collisions
Prefix event names with your app’s namespace (e.g., App.User.registered) to avoid conflicts with third-party packages.
Event Dumping Log event data for debugging:
$eventManager->on('*.*', function ($event) {
Log::debug('Event triggered:', ['name' => $event->name, 'data' => $event->data]);
});
Listener Tracing Track which listeners are attached:
$listeners = $eventManager->listeners('User.registered');
// $listeners = [['target' => $listener, 'callback' => 'handle']]
Default Middleware
CakePHP’s EventManager doesn’t support Laravel’s middleware groups, but you can emulate this by chaining listeners with priorities.
Async Events For async processing, combine with Laravel’s queues:
$eventManager->on('Order.placed', function ($event) {
dispatch(new \App\Jobs\ProcessOrder($event->data))->delay(now()->addSeconds(5));
});
Custom Event Classes
Extend \Cake\Event\Event to add metadata:
class AppEvent extends \Cake\Event\Event
{
public function __construct($name, $target, array $data = [], public int $retries = 0)
{
parent::__construct($name, $target, $data);
}
}
Event Filters Add filtering logic to events (e.g., only dispatch if a condition is met):
$eventManager->on('User.updated', function ($event) {
if ($event->data['is_admin']) {
// Handle admin updates
}
});
Global Event Hooks Attach a listener to all events using wildcards:
$eventManager->on('*.*', [$globalListener, 'logAllEvents']);
How can I help you explore Laravel packages today?