Depuis toujours , Drupal fournit une multitude de hooks qui donnent la possibilité de procéder à un traitement spécifique lié à une action donnée.
Avant d'attaquer l'Api Event, Il faut préciser que les hooks ne sont que des fonctions PHP qui permettent d’interagir avec le cœur, les modules ainsi que les thèmes de Drupal.
Exemple des 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();
}
}
Dans cet exemple, le hookENTITYinsert nous a permis d'interagir avec l'entité Node, en modifiant cette dernière avant qu'elle soit sauvegardée dans la base de donnée. Il faut pas nier que le système des hooks est surpuissant, vous allez vous demander pourquoi alors adopter l'API Event !
Comme on l'avait vu dans la section précédente; le système des hooks à fait ses preuves dans les anciennes versions de Drupal, mais avec l'arrivée de Drupal 8, le plus grand défi était de refaire le CMS d'une façon à ce qu'il adopte le paradigme d'orienté objet, dans ce cadre Drupal a introduit l'API Event de Symfony dans sa version 8 toute en gardant le système des hooks qui est supposé être totalement supprimer dans la version 9 de Drupal.
Théoriquement, il existe un registre d'événements appelé Event Registry
1. Event dispatcher
Comme on l'avait déjà cité, l'API Event nous permet de propager un événement dans notre site afin qu'on puisse l'écouter par un listener
Créons ensemble notre module Custom events qui va contenir tous nos travaux sur l'Event API:
name: Custom events
type: module
description: Example events distpacher and subscriber.
core: 8.x
package: Custom
Il faut définir la classe d'événement dans le répertoire src/Event
dans la racine du module, dans notre cas c'est 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 "Ceci est un exemple d'événement.";
}
}
Symfony\Component\EventDispatcher\Event
C'est ici que se cache la puissance de API Event, notre événement peut être propager n'importe où dans notre application, comment? voyons voir ensemble :
<?php
// On utilise le namespace de notre event (ExampleEvent)
use Drupal\example_events\ExampleEvent;
// On charge le service event_dispatcher
$dispatcher = \Drupal::service('event_dispatcher');
// On crée une nouvelle instance de notre event (ExampleEvent)
$event = new ExampleEvent($form_state->getValue('name'));
// On dispatche l'événement via la méthode "dispatch" et on passe dedant le nom de l'événement qu'on souhaite propager et l'objet d'événement '$event' comme paramètre.
$dispatcher->dispatch(ExampleEvent::SUBMIT, $event);
Le code snippet si-dessus montre comment dispatcher un événement n'importe où dans notre site.
Dans ce tutoriel, on va créer un formulaire dans lequel on dispatchera notre événement et plus précisément dans le callback submitForm, on verra ça toute de suite :
<?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 DemoEventDispatchForm.
*
* @package Drupal\example_events\Form
*/
class EventDispatchForm extends FormBase {
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'event_dispatch_form';
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$form['reference'] = array(
'#type' => 'textfield',
'#title' => $this->t('Reference'),
'#description' => $this->t("Veuillez écrire quelque chose qui servira comme object d'événement"),
'#maxlength' => 64,
'#size' => 64,
);
$form['dispatch_action'] = array(
'#type' => 'submit',
'#value' => $this->t('Dispatcher'),
);
return $form;
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
// On dispatche notre événement
$dispatcher = \Drupal::service('event_dispatcher');
$event = new ExampleEvent($form_state->getValue('reference'));
$dispatcher->dispatch(ExampleEvent::SUBMIT, $event);
}
}
Il est temps de créer notre première Route sur laquelle on affichera notre formulaire:
example_events.event_dispatcher_form:
path: 'event-dispatch-form'
defaults:
_form: '\Drupal\example_events\Form\EventDispatchForm'
_title: 'Event Dispatcher Form'
requirements:
_permission: 'access content'
Parfait, jusqu'ici nous avons réussi à créer notre Event et le dispatcher une fois que notre formulaire est enregistré (submitForm callback).
2. Event Subscriber
2.1 Création de la classe de l'Event Subscriber Regardons ensemble maintenant comment on peut souscrire à l'événement qu'on a déjà créé (ExampleEvent).
Pour souscrire à un événement existant, il faudra créer la classe de l'Event Subscriber sur /ModuleRacine/src/EventSubscriber/
, dans notre example c'est sur /example_events/src/EventSubscriber/ExampleEventSubscriber
<?php
/**
* @file
* Contains \Drupal\project\ExampleEventSubscriber.
*/
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.
*/
class ExampleEventSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
return [
ConfigEvents::SAVE => ['savingConfig', 800],
ExampleEvent::SUBMIT => array('executeAction', 800)
];
}
/**
* Subscriber Callback for the event.
*/
public function executeAction(ExampleEvent $event) {
\Drupal::messenger()->addStatus(t("Vous avez souscrit à l'événement ExampleEvent qui a été dispatché lors de l'enregistrement du formulaire avec " . $event->getReferenceID() . " comme référence "));
}
/**
* Subscriber Callback for the event.
*/
public function savingConfig(ConfigCrudEvent $event) {
\Drupal::messenger()->addStatus(t("La configuration " . $event->getConfig()->getName() . " a été sauvegardée"));
}
}
Top, regardons ensemble ce qu'on a fait dans cette classe :
Symfony\Component\EventDispatcher\EventSubscriberInterface
.Vous allez certainement vous demander d’où vient ConfigEvents::SAVE
; c'est un événement existant par défaut dispatché par le cœur de Drupal.
C'est une bonne pratique de définir les nouveaux événements qu'on est entrain de créer comme des constantes pour qu'ils soient globalement disponibles dans notre classe.
2.2 Taguer la classe de l'Event Subscriber par event_subscriber
services:
example_events.event_subscriber_example:
class: Drupal\example_events\EventSubscriber\ExampleEventSubscriber
tags:
- { name: 'event_subscriber' }
Ci-dessus nous avons créer notre premier service example_events.event_subscriber_example
dans example_events.services.yml
et on a tagué ce dernier par event_subscriber
, comme ça notre service est enregistré comme un EventSubscriber dans le service container.
Voici à quoi ressemble notre formulaire après enregistrement d'une référence:
Code source du module disponible sur Github :)