You've defined the event and dispatched it — now build the piece that actually does something when it fires. An event subscriber is a plain service, tagged so Drupal knows to ask it which events it cares about, that runs one of its methods whenever a matching event is dispatched anywhere in the system.
What you'll learn in this lesson
- The two things a subscriber needs: implementing
EventSubscriberInterfaceand a services.yml tag - How
getSubscribedEvents()maps an event name to one or more handler methods - How to read data off the event object inside a handler method
- What
$event->stopPropagation()does, and when to reach for it
Registering the subscriber service
services:
my_module.incident_subscriber:
class: Drupal\my_module\EventSubscriber\IncidentSubscriber
tags:
- { name: event_subscriber }
Same tag you saw for core kernel events a few lessons back — event_subscriber works identically whether you're listening to Drupal core or to your own custom event. This is one of the nice consistencies of the system: once you know how to subscribe to one kind of event, you know how to subscribe to all of them.
The subscriber class
<?php
namespace Drupal\my_module\EventSubscriber;
use Drupal\my_module\Event\IncidentEvents;
use Drupal\my_module\Event\IncidentReportEvent;
use Drupal\Core\Messenger\MessengerTrait;
use Drupal\Core\StringTranslation\StringTranslationTrait;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class IncidentSubscriber implements EventSubscriberInterface {
use StringTranslationTrait;
use MessengerTrait;
public static function getSubscribedEvents(): array {
$events[IncidentEvents::NEW_REPORT][] = ['notifyMario'];
$events[IncidentEvents::NEW_REPORT][] = ['notifyBatman', -100];
$events[IncidentEvents::NEW_REPORT][] = ['notifyDefault', -255];
return $events;
}
public function notifyMario(IncidentReportEvent $event) {
if ($event->getType() == 'stolen_princess') {
$this->messenger()->addStatus($this->t('Mario has been alerted.'));
$event->stopPropagation();
}
}
public function notifyBatman(IncidentReportEvent $event) {
if ($event->getType() == 'joker') {
$this->messenger()->addStatus($this->t('Batman has been alerted.'));
$event->stopPropagation();
}
}
public function notifyDefault(IncidentReportEvent $event) {
$this->messenger()->addStatus($this->t('Thank you for reporting this incident.'));
$event->stopPropagation();
}
}
getSubscribedEvents() — a map, not a hook
This static method is the entire contract of EventSubscriberInterface. It returns an array keyed by event name; each key maps to one or more [method_name, priority] pairs (priority is optional and defaults to 0). A single subscriber class can react to any number of different events by adding more keys — you don't need a separate class per event, only per logical grouping of related reactions.
Reading the event, and stopping propagation
Each handler method receives the exact event object that was dispatched — here, an IncidentReportEvent — and can call its getters (getType(), getReport()) to decide how to react. $event->stopPropagation() tells the dispatcher "no further subscribers for this event should run" — useful when one subscriber's reaction makes any later ones redundant or actively wrong, but be careful with it: stopping propagation means any OTHER unrelated module's subscriber to the same event silently never runs either.
IncidentEvents::NEW_REPORT event, each checking a different condition and stopping propagation once one matches. This "chain of increasingly generic handlers, ending in a catch-all" pattern — Mario, then Batman, then a default fallback — is worth recognizing; you'll see the same shape in Drupal core's own access-checking subscribers.Quick check: if you reported a "Cat stuck in tree" incident (type
cat), which of the three methods above actually shows a message — and why? (Answer: onlynotifyDefault().notifyMario()checks forstolen_princessandnotifyBatman()checks forjoker— neither matches, so neither stops propagation, letting the event fall through to the priority-255catch-all.)
Key takeaways
- A subscriber is a plain service implementing
EventSubscriberInterface, taggedevent_subscriberin services.yml — the same tag whether the event is core or custom. getSubscribedEvents()maps event names to[method, priority]pairs; one class can handle multiple events or multiple conditions on the same event.- Handler methods receive the exact event object that was dispatched and can call its getters to inspect the data it carries.
$event->stopPropagation()halts remaining subscribers for that event entirely — including ones from other, unrelated modules — so use it deliberately.
Coming up next
What happens when two subscribers from different modules both want to react to the same event, and the order matters? Next, you'll control exactly that with subscriber priorities.