9.12. Event Dispatcher, aangepaste code voor specifieke gebeurtenissen
Het eventsysteem in Drupal maakt het mogelijk om complexere systemen te bouwen, waarbij functionaliteit kan worden aangepast met eigen code die reageert op specifieke gebeurtenissen. Veel hooks uit Drupal 7 zijn vervangen door events. Dit heeft gezorgd voor een uniformere manier van werken binnen Drupal en in uitbreidingsmodules. Het eventsysteem is afkomstig uit Symfony en bestaat uit de volgende onderdelen:
Event Subscribers – “abonnees” op specifieke gebeurtenissen. Dit zijn functies of methoden die worden uitgevoerd wanneer een bepaalde gebeurtenis plaatsvindt. In de code is dit een klasse die de volgende interface implementeert:
\Symfony\Component\EventDispatcher\EventSubscriberInterface
Event Registry – verzamelt en sorteert Event Subscribers op basis van hun prioriteit (de volgorde waarin ze worden uitgevoerd). De registry voor subscribers wordt opgeslagen in het Event Dispatcher-object als een array van sleutels (eventnamen) en hun prioriteit. Wanneer een event als service wordt geregistreerd, is deze dispatcher wereldwijd beschikbaar.
Event Dispatcher – het mechanisme dat de gebeurtenis triggert en de juiste Event Subscribers aanroept op het juiste moment. Minstens één instantie van de Event Dispatcher wordt als service aangeboden. De Event Dispatcher-klasse implementeert de interface:
\Symfony\Component\EventDispatcher\EventDispatcherInterface
Event Context – veel events vereisen specifieke gegevens die van belang zijn voor de subscriber. Dit kan een eenvoudige waarde zijn of een klasse die extra informatie bevat. De Event Context-klasse breidt de klasse uit:
\Symfony\Component\EventDispatcher\Event
Laten we enkele voorbeelden bekijken om te begrijpen hoe dit werkt.
Ik heb alle code toegevoegd aan de module drupalbook_examples op GitHub. Je kunt de module downloaden en aan je site toevoegen:
https://github.com/levmyshkin/drupalbook8
Drupal 8 bevat geen hook_init()
meer:
https://www.drupal.org/node/2013014
Nu kun je met een Event Subscriber code uitvoeren bij het laden van een pagina:
modules/custom/drupalbook_examples/drupalbook_examples.services.yml
services:
drupalbook_examples.event_subscriber:
class: Drupal\drupalbook_examples\EventSubscriber\DrupalbookExamplesSubscriber
tags:
- {name: event_subscriber}
Om een Event Subscriber te koppelen, moet je de service registreren in het bestand *.services.yml van je module. Hier geef je aan welke klasse de subscriber implementeert en voeg je het label event_subscriber toe onder tags
. Vervolgens maken we de event subscriber-klasse aan:
modules/custom/drupalbook_examples/src/EventSubscriber/DrupalbookExamplesSubscriber.php
<?php
namespace Drupal\drupalbook_examples\EventSubscriber;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class DrupalbookExamplesSubscriber implements EventSubscriberInterface {
public function checkForRedirection(GetResponseEvent $event) {
if ($event->getRequest()->query->get('redirect-me')) {
$event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('checkForRedirection');
return $events;
}
}
Belangrijke punten in deze code: de klasse van onze event subscriber implementeert de interface EventSubscriberInterface. De belangrijkste methode daarin is getSubscribedEvents()
, die bepaalt op welke events onze subscriber reageert en welke methoden moeten worden uitgevoerd wanneer een event plaatsvindt. In dit geval luisteren we naar het KernelEvents::REQUEST-event, dat aan het begin van de Drupal-verwerking plaatsvindt. Als het event optreedt, wordt de methode checkForRedirection()
uitgevoerd. In deze methode controleren we of de parameter redirect-me
aanwezig is en voeren we een redirect uit naar de startpagina.
Deze code werkt telkens wanneer een pagina wordt geladen – of bijna altijd. Wanneer de pagina echter vanuit de cache wordt geserveerd, wordt deze code niet uitgevoerd omdat Drupal de output direct uit de cache haalt.
In zo’n geval kun je gebruikmaken van een alternatief voor de verwijderde hook hook_boot()
, die, net als hook_init()
, niet meer bestaat in Drupal 8:
https://www.drupal.org/node/1909596
Laten we daarom een extra methode toevoegen: redirectBeforeWithoutCache()
modules/custom/drupalbook_examples/src/EventSubscriber/DrupalbookExamplesSubscriber.php:
<?php
namespace Drupal\drupalbook_examples\EventSubscriber;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\HttpKernel\KernelEvents;
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class DrupalbookExamplesSubscriber implements EventSubscriberInterface {
public function checkForRedirection(GetResponseEvent $event) {
if ($event->getRequest()->query->get('redirect-me')) {
$event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
}
}
public function redirectBeforeWithoutCache(GetResponseEvent $event) {
if ($event->getRequest()->query->get('redirect-me')) {
$event->setResponse(new RedirectResponse(\Drupal::url('<front>', [], ['absolute' => TRUE])));
}
}
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[KernelEvents::REQUEST][] = array('checkForRedirection');
$events[KernelEvents::REQUEST][] = array('redirectBeforeWithoutCache', 300);
return $events;
}
}
In de methode getSubscribedEvents()
hebben we nu twee methoden geregistreerd die op hetzelfde event reageren: KernelEvents::REQUEST. In het tweede geval hebben we echter de prioriteit ingesteld op 300, zodat deze methode eerder wordt uitgevoerd dan andere listeners met een lagere prioriteit. Hierdoor werkt de redirect zelfs wanneer de pagina uit de cache wordt geladen.
Er zijn in Drupal veel verschillende events waarop je kunt abonneren. Meer voorbeelden en een overzicht van beschikbare events vind je in de officiële documentatie: