Drupal 11 - Utiliser la nouvelle syntaxe Orientée Objet pour les Hooks
Drupal 11.1 va introduire une nouvelle façon d'implémenter les hooks, de manière orientée objet !
En pratique il suffira de créer un fichier MesHooks.php (un ou plusieurs fichiers, le nom est libre) dans le dossier src/Hook (cela peut être dans un sous dossier, mais le dossier de base doit être obligatoirement Hook pour être détecté par drupal) de votre module.
Ainsi pour implémenter le hook HOOK_preprocess_node, voici comment cela se passe :
Je créé un fichier mon_module/src/Hook/NodeHooks.php avec le contenu suivant :
<?php
namespace Drupal\mon_module\Hook;
use Drupal\Core\Hook\Attribute\Hook;
/**
* Provides hook implementations for Node entities.
*/
class NodeHooks {
/**
* Prepares the node variables for rendering.
*
* @param array $variables
* The variables array to preprocess, including the node entity.
*/
#[Hook('preprocess_node')]
public static function preprocessNode(&$variables): void {
$variables['content']['publication_date'] = [
'#theme' => 'publication_date',
'#created' => $variables['node']->get('created')->value,
'#changed' => $variables['node']->get('changed')->value,
];
}
}
c'est l'annotation
#[Hook('preprocess_node')]
qui indiquera à drupal quel est le hook implémenté par la fonction qui suit (dont le nom est libre)
On peut aussi utiliser une même fonction pour plusieurs hooks :
#[Hook('comment_insert')]
#[Hook('comment_update')]
public function commentInsertOrUpdate(CommentInterface $comment) {
...
}
La fin du fichier module ?
Presque, comme indiqué dans le change record, tous les hooks ne peuvent pas être implémenté de cette manière là, doivent rester en procédural :
hook_cache_flush()hook_install()hook_module_preinstall()hook_module_preuninstall()hook_modules_installed()hook_modules_uninstalled()hook_post_update_NAME()hook_requirements()hook_schema()hook_uninstall()hook_update_last_removed()hook_update_N()hook_theme_suggestion_HOOK()hook_theme_suggestions_HOOK_alter()
Compatibilité avec Drupal 10 et 11.0
Cette syntaxe ne fonctionne qu'à partir de Drupal 11.1, qui devrait sortir en décembre 2024, mais bonne nouvelle, il est possible de l'utiliser dès aujourd'hui en attendant de faire l'upgrade. Par contre dommage, il faut pour cela garder l'implémentation original qui appellera notre classe.
On commence par ajouter notre classe à notre fichier mon_module.services.yml :
services:
Drupal\mon_module\Hook\NodeHooks:
class: Drupal\mon_module\Hook\NodeHooks
autowire: true
Et on ajoute l'appel à la classe dans mon_module.module :
/**
* Implements hook_preprocess_node().
*/
#[LegacyHook]
function mon_module_preprocess_node(&$variables) {
\Drupal::service(Drupal\mon_module\Hook\NodeHooks::class)->preprocessNode($variables);
}
Une fois la migration vers drupal 11.1 faite ces deux ajustements pourront être supprimés.
À noter, pour que les annotations soient connues par Drupal (et que phpstan valide votre code), il est nécessaire d'utiliser le patch présent sur cet issue : https://www.drupal.org/project/drupal/issues/3482464 qui backport les déclarations d'annotations : https://git.drupalcode.org/project/drupal/-/merge_requests/9908.patch
Voir le change record : https://www.drupal.org/node/3442349