Drupal 11 - Utiliser la nouvelle syntaxe Orientée Objet pour les Hooks

Implémentation orientée objet des hook

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à. Certains doivent rester en procédural. Même si comme l'on voit sur ce nouveau changement du 12 novembre, la liste commence à ce réduire (en barré, les hooks qui peuvent maintenant bénéficier de cette nouvelle syntaxe.

  • hook_cache_flush()
  • hook_module_preinstall()
  • hook_module_preuninstall()
  • hook_modules_installed()
  • hook_modules_uninstalled()
  • hook_install()
  • 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()
     

La liste devrait être mise à jour sur la page suivante : https://www.drupal.org/node/3442349

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

Petite astuce pour les performance

Nouveauté du 3 décembre 2024, il existe maintenant une annotation pour signaler à drupal qu'il n'est pas nécessaire de scanner un fichier à la recherche d'implémentation de hooks (principalement les fichiers .module) On est sur l'ordre de la micro-optimisation mais bon, ça peut servir.

Si vous n'utilisez aucun hook procédural (ou uniquement à visé de rétrocompatibilité pour les drupaux < 11.1, vous pouvez ajouter à votre fichier services.yml la clé suivante : 

parameters:
  module_name.hooks_converted: true

Si certains de vos fichiers contiennent encore des hooks procéduraux, alors placez-les tous au début de votre fichier, ajoutez à vos use

use Drupal\Core\Hook\Attribute\StopProceduralHookScan;

Et juste après votre dernier hook procédural, placez l'annotation suivante : 

#[StopProceduralHookScan]

Ainsi drupal saura qu'il n'a pas besoin de scanner le reste du fichier à la recherche de potentiels hooks.

Le change record : https://www.drupal.org/node/3490771

Contenus en rapport

Ajouter un commentaire

Ne sera pas publié
CAPTCHA
Désolé, pour ça, mais c'est le seul moyen pour éviter le spam...