Drupal has always provided a wealth of hooks that allow specific processing tied to a given action.
Before diving into the Event API, it is important to clarify that hooks are simply PHP functions that allow you to interact with Drupal's core, modules, and themes.
Example of hooks:
/**
* Implements hook_ENTITY_insert().
*
*/
function hook_entity_insert(Drupal\Core\Entity\EntityInterface $entity) {
if ($entity->getEntityType()->id() == 'node' && $entity->bundle() == "article") {
$entity->setTitle($entity->label() . ' ' . date("Y/m/d"));
$entity->save();
}
}In this example, hookENTITYinsert allowed us to interact with the Node entity, modifying it before it was saved to the database. It is hard to deny that the hook system is extremely powerful — so you might wonder why we would adopt the Event API instead!
As we saw in the previous section, the hook system proved itself in older versions of Drupal. However, with the arrival of Drupal 8, the biggest challenge was rebuilding the CMS to adopt the object-oriented paradigm using Symfony framework components. In this context, Drupal introduced Symfony's Event API in version 8, while keeping the hook system — which was supposed to be completely removed in Drupal 9.
Theoretically, there is an event registry called the Event Registry
1. Event Dispatcher
As mentioned earlier, the Event API allows us to propagate an event throughout our site so it can be listened to by a listener.
Let us create our Custom Events module, which will contain all our Event API work:
name: Custom events
type: module
description: Example events dispatcher and subscriber.
core: 8.x
package: CustomWe need to define the event class in the src/Event directory within the module root — in our case custom_events/src/Event/ExampleEvent.php:
<?php
namespace Drupal\project\Event;
use Symfony\Component\EventDispatcher\Event;
class ExampleEvent extends Event
{
const SUBMIT = 'event.submit';
protected $referenceID;
public function __construct($referenceID)
{ $this->referenceID = $referenceID;
}
public function getReferenceID()
{ return $this->referenceID;
}
public function myEventMessage()
{ return "This is an example event.";
}
}Symfony\Component\EventDispatcher\Event.$referenceID variable, which will be accessible by the Event Subscriber once our event is dispatched.This is where the power of the Event API lies — our event can be dispatched from anywhere in our application:
<?php
// Use our event namespace (ExampleEvent)
use Drupal\example_events\ExampleEvent;
// Load the event_dispatcher service
$dispatcher = \Drupal::service('event_dispatcher');
// Create a new instance of our event (ExampleEvent)
$event = new ExampleEvent($form_state->getValue('name'));
// Dispatch the event via the "dispatch" method, passing the event name and the '$event' object as parameter.
$dispatcher->dispatch(ExampleEvent::SUBMIT, $event);The snippet above shows how to dispatch an event from anywhere in your site.
In this tutorial, we will create a form in which we dispatch our event — specifically in the submitForm callback:
<?php
/**
* @file
* Contains \Drupal\example_events\Form\DemoEventDispatchForm.
*/
namespace Drupal\project\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\example_events\Event\ExampleEvent;
class EventDispatchForm extends FormBase {
public function getFormId() {
return 'event_dispatch_form';
}
public function buildForm(array $form, FormStateInterface $form_state) {
$form['reference'] = array(
'#type' => 'textfield',
'#title' => $this->t('Reference'),
'#description' => $this->t("Please write something that will serve as the event object"),
'#maxlength' => 64,
'#size' => 64,
);
$form['dispatch_action'] = array(
'#type' => 'submit',
'#value' => $this->t('Dispatch'),
);
return $form;
}
public function submitForm(array &$form, FormStateInterface $form_state) {
// Dispatch our event
$dispatcher = \Drupal::service('event_dispatcher');
$event = new ExampleEvent($form_state->getValue('reference'));
$dispatcher->dispatch(ExampleEvent::SUBMIT, $event);
}
}Time to create our first Route, on which we will display our form:
example_events.event_dispatcher_form:
path: 'event-dispatch-form'
defaults:
_form: '\Drupal\example_events\Form\EventDispatchForm'
_title: 'Event Dispatcher Form'
requirements:
_permission: 'access content'So far, we have successfully created our Event and dispatched it when the form is submitted (submitForm callback).
2. Event Subscriber
2.1 Creating the Event Subscriber class Let us now look at how to subscribe to the event we already created (ExampleEvent).
To subscribe to an existing event, we need to create the Event Subscriber class at /ModuleRoot/src/EventSubscriber/ — in our example: /example_events/src/EventSubscriber/ExampleEventSubscriber
<?php
namespace Drupal\example_events\EventSubscriber;
use Drupal\Core\Config\ConfigCrudEvent;
use Drupal\Core\Config\ConfigEvents;
use Drupal\example_events\Event\ExampleEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class ExampleEventSubscriber implements EventSubscriberInterface {
public static function getSubscribedEvents() {
return [
ConfigEvents::SAVE => ['savingConfig', 800],
ExampleEvent::SUBMIT => array('executeAction', 800)
];
}
public function executeAction(ExampleEvent $event) {
\Drupal::messenger()->addStatus(t("You subscribed to the ExampleEvent dispatched when the form was submitted with " . $event->getReferenceID() . " as the reference."));
}
public function savingConfig(ConfigCrudEvent $event) {
\Drupal::messenger()->addStatus(t("The configuration " . $event->getConfig()->getName() . " has been saved."));
}
}Here is what we did in this class:
Symfony\Component\EventDispatcher\EventSubscriberInterface.You may wonder where ConfigEvents::SAVE comes from — it is an existing event dispatched by Drupal core.
It is good practice to define new events as constants so they are globally available in our class.
2.2 Tagging the Event Subscriber class with event_subscriber
services:
example_events.event_subscriber_example:
class: Drupal\example_events\EventSubscriber\ExampleEventSubscriber
tags:
- { name: 'event_subscriber' }Above, we created our first service example_events.event_subscriber_example in example_events.services.yml and tagged it with event_subscriber, registering our service as an EventSubscriber in the service container.
Source code available on Github :)