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
Pour un projet, je souhaiter alterer un type d'entité via HOOK_entity_type_alter. Problème un module du core effectuait une modification sur ce même type d'entité, et son hook était exécuté après celui de mon module.
Ajouter un commentaire