Dispatching events

Since we have discussed how to subscribe to events in Drupal 8, we should also take a look at how we can dispatch our own events. After all, the Symfony Event Dispatcher component is one of the principal vectors of extensibility in Drupal 8.

To demonstrate this, we will create an event to be dispatched whenever our HelloWorldSalutation::getSalutation() method is called. The purpose is to inform other modules that this has happened and potentially allow them to alter the message that comes out of the configuration object—not really a solid use case, but good enough to demonstrate how we can dispatch events.

The first thing that we will need to do is to create an event class that will be dispatched. It can go into the root of our module's namespace:

namespace Drupal\hello_world; 
 
use Symfony\Component\EventDispatcher\Event; 
 
/** 
 * Event class to be dispatched from the HelloWorldSalutation service. 
 */ 
class SalutationEvent extends Event { 
 
  const EVENT = 'hello_world.salutation_event'; 
 
  /** 
   * The salutation message. 
   * 
   * @var string 
   */ 
  protected $message; 
 
  /** 
   * @return mixed 
   */ 
  public function getValue() { 
    return $this->message; 
  } 
 
  /** 
   * @param mixed $message 
   */ 
  public function setValue($message) { 
    $this->message = $message; 
  } 
}  

The main purpose of this event class is that an instance of it will be used to transport the value of our salutation message. This is why we created the $message property on the class and added the getter and setter methods. Moreover, we use it to define a constant for the actual name of the event that will be dispatched. Finally, the class extends from the base Event class that comes with the Event Dispatcher component as a standard practice. We could also use that class directly, but we would not have our data stored in it as we do now.

Next, it's time to inject the Event Dispatcher service into our HelloWorldSalutation service. We have already injected config.factory, so we just need to add a new argument to the service definition:

arguments: ['@config.factory', '@event_dispatcher'] 

Of course, we will also receive it in the constructor and store it as a class property:

/** 
 * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface 
 */ 
protected $eventDispatcher; 
 
/** 
 * HelloWorldSalutation constructor. 
 * 
 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory 
 * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $eventDispatcher 
 */ 
public function __construct(ConfigFactoryInterface $config_factory, EventDispatcherInterface $eventDispatcher) { 
  $this->configFactory = $config_factory; 
  $this->eventDispatcher = $eventDispatcher; 
} 

We will also have the obligatory use statement for the EventDispatcherInterface at the top of the file:

use Symfony\Component\EventDispatcher\EventDispatcherInterface;  

Now, we can make use of the dispatcher. So instead of the following code inside the getSalutation() method:

if ($salutation != "") { 
  return $salutation; 
}  

We can have the following:

if ($salutation != "") { 
  $event = new SalutationEvent(); 
  $event->setValue($salutation); 
  $event = $this->eventDispatcher->dispatch(SalutationEvent::EVENT, $event); 
  return $event->getValue(); 
}

So with the above, we decided that if we are to return a salutation message from the configuration object, we want to inform other modules and allow them to change it. We first create an instance of our Event class and feed it the relevant data (the message). Then, we dispatch the named event and pass the event object along with it. The Event Dispatcher returns the event that has been dispatched with any changes that might have been applied to it by subscribers. Finally, we get the data from that instance and return it.

Pretty simple, isn't it? What can subscribers do? It's very similar to what we saw regarding the example on redirects in the preceding section. All a subscriber needs to do is listen for the SalutationEvent::EVENT event and do something based on that. The main thing that it can do is use the setValue() method on the received event object to change the salutation message. It can also use the stopPropagation() method from the base Event class to inform the Event Dispatcher to no longer trigger other listeners that have subscribed to this event.