This tutorial explores various examples of dot-event usages.
To start using events you will need the following things:
ConfigProvider
)The listeners needs to be registered in the ConfigProvider
, under the
['dot-event']
key
The below example will implement an event for update users.
Every event needs to extends Dot\Event\Event
class UserEvent extends Event
{
public const EVENTS_PRE_UPDATE = 'pre.update.user';
public const EVENTS_POST_UPDATE = 'post.update.user';
public function __construct($name = null, $target = null, $params = [])
{
parent::__construct($name, $target, $params);
}
}
We use the concept of listener aggregates because with this approach in a single class
we can listen to multiple events. If you pay attention, in the above event we have 2 events
pre.update.user
and post.update.user
Every listener needs to extend Dot\Event\ControllerEventListenerInterface
. The interface
defines two methods attach()
and detach()
class UserEventListener implements DotEventListenerInterface
{
use ListenerAggregateTrait;
#[Dot\AnnotatedServices\Attribute\Inject(Logger::class)]
public function __construct(private Logger $logger)
{
}
public function attach(EventManagerInterface $events, $priority = 1)
{
$this->listeners[] = $events->attach(UserUpdatedEvent::EVENTS_PRE_UPDATE, [$this, 'onUserPreUpdated']);
$this->listeners[] = $events->attach(UserUpdatedEvent::EVENTS_POST_UPDATE, [$this, 'onUserPostUpdate']);
}
public function onUserPreUpdated(UserEvent $event)
{
$this->logger->info('The pre update event is triggered');
//...more logic
}
public function onUserPostUpdate(UserEvent $event)
{
//...more logic
$this->logger->info('The post update event is triggered');
}
}
NOTE
The trait
Laminas\EventManager\ListenerAggregateTrait
can be used to help implementingDotEventListenerInterface
. It defines the$listeners
property, anddetach()
logic
We register the listener in the ConfigProvider
'dot-event' => [
UserEventListener::class
]
Every event can be triggered from an Laminas\EventManager\EventManager
instance loaded
from the container
class MyService
{
#[Dot\AnnotatedServices\Attribute\Inject(EventManagerInterface::class)]
public function __construct(private EventManagerInterface $eventManager)
{
}
public function update()
{
$this->eventManager->triggerEvent(new UserEvent(UserEvent::EVENTS_PRE_UPDATE, params: ['user' => $user]));
// ... logic of update
$this->eventManager->triggerEvent(new UserEvent(UserEvent::EVENTS_POST_UPDATE, params: ['user' => $user]));
}
}
NOTE
To inject classes from the container we use
Dot\AnnotatedServices\Attribute\Inject
fromdot-annotated-services
package, but you can use your own logic to get things from the container
You can attach multiple listeners to the same event but with different logic.
All listeners are executed in the order in which they are attached. However, you can provide a priority value and you can influence the order of the execution
class UserEventListener implements DotEventListenerInterface
{
use ListenerAggregateTrait;
#[Dot\AnnotatedServices\Attribute\Inject(Logger::class)]
public function __construct(private Logger $logger)
{
}
public function attach(EventManagerInterface $events, $priority = 1)
{
$this->listeners[] = $events->attach(UserUpdatedEvent::EVENTS_POST_UPDATE, [$this, 'onUserPostUpdateSecond'], 1); // will run second
$this->listeners[] = $events->attach(UserUpdatedEvent::EVENTS_POST_UPDATE, [$this, 'onUserPostUpdateFirst'], 2); // will run first
}
// ... callbacks
}
As you can notice, we attach the same event name to listeners, so once we trigger the event both callback will run one after another. But because we provide priority, the second attach will run first because has a higher priority.
Sometimes you have more listeners to an event, and you may want to stop the execution of the event if something is wrong in one of the listeners.
public function onUserPostUpdateFirst(UserEvent $event)
{
$event->stopPropagation();
}
public function onUserPostUpdateSecond(UserEvent $event)
{
// this will not execute
}
You can return whatever you want in a listener callback. All events trigger returns an instance of
Laminas\EventManager\ResponseCollection
so you can have information if the event has stopped, or if event
returned some expected object.
class MyService
{
#[Dot\AnnotatedServices\Attribute\Inject(EventManagerInterface::class)]
public function __construct(private EventManagerInterface $eventManager)
{
}
public function update()
{
/** @var Laminas\EventManager\ResponseCollection $result */
$result = $this->eventManager->triggerEvent(new UserEvent(UserEvent::EVENTS_POST_UPDATE, params: ['user' => $user]));
$result->stopped(); // true or false if propagation is stopped
$result->first(); // what last listener has returned (on multiple listeners it uses LIFO mode)
}
}