Dispatching Custom Events in Drupalfor Drupal 11 , 10 , 9 , and 8

Last updated :  

You've defined the event class — now it's time to actually fire it. Dispatching a custom event is deliberately anticlimactic: two lines of code, using a service Drupal already provides. All the interesting behavior lives in whatever subscribers happen to be listening, which is exactly the point — the code that dispatches an event should stay completely ignorant of what happens next.

What you'll learn in this lesson

  • How to inject the event_dispatcher service into a form or controller
  • The exact two-line pattern for constructing and dispatching an event
  • Why the code that dispatches an event should never know or care who's listening

A real dispatch, inside a form submit handler

use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Drupal\my_module\Event\IncidentEvents;
use Drupal\my_module\Event\IncidentReportEvent;

class IncidentReportForm extends FormBase {

  protected $eventDispatcher;

  public function __construct(EventDispatcherInterface $event_dispatcher) {
    $this->eventDispatcher = $event_dispatcher;
  }

  public static function create(ContainerInterface $container) {
    return new static($container->get('event_dispatcher'));
  }

  public function submitForm(array &$form, FormStateInterface $form_state) {
    $type = $form_state->getValue('incident_type');
    $report = $form_state->getValue('incident');

    $event = new IncidentReportEvent($type, $report);
    $this->eventDispatcher->dispatch($event, IncidentEvents::NEW_REPORT);
  }

}

The event_dispatcher service is injected exactly like any other service, via create() and the constructor — nothing event-specific about that part. Inside submitForm(), the dispatch itself is two lines: construct the event object with whatever data subscribers will need, then hand it to $this->eventDispatcher->dispatch() along with the event's name constant.

Why the dispatcher, not a direct method call?

You could, in theory, just call a method directly if you knew exactly which class needed to react. But that would mean this form has to know about every module that might ever want to respond to an incident report — including modules that don't exist yet. The dispatcher inverts that dependency: this code depends only on the event dispatcher service and its own event class, and Drupal's service container handles wiring up whichever subscribers happen to be tagged for this event, with zero changes required here when a new one is added later.

Argument order matters and has changed over Drupal's history: modern Symfony (used by Drupal 9.3+) expects dispatch($event, $event_name) — the event object first, the name second. Older code you might encounter from Drupal 8/early 9 sometimes has these reversed. If a dispatch call silently does nothing, check this first.

Quick check: at the moment $this->eventDispatcher->dispatch(...) is called, does this form code know whether zero, one, or ten subscribers are listening? (Answer: no, and it shouldn't — that's the whole design. The dispatch call succeeds identically whether nothing is listening or five modules are, which is exactly what keeps this form decoupled from every possible reactor.)

Key takeaways

  • Dispatching is always two steps: construct the event object with the data subscribers will need, then call $dispatcher->dispatch($event, EventNameClass::CONSTANT).
  • Inject the event_dispatcher service the same way you'd inject any other service — via create() and the constructor.
  • Modern Drupal/Symfony expects the event object as the first argument and the event name second — the reverse of older code you may find in older tutorials.
  • Code that dispatches an event should have zero knowledge of what, if anything, is listening — that decoupling is the entire benefit of using events over direct method calls.

Coming up next

An event with nobody listening does nothing. Next, you'll build the event subscriber service that actually reacts when this event fires.