Drupal - Structuration d'un module

Écrit le 02/03/2022
  1. Qu’est-ce qu’un module ?

Avant de créer un module custom, il faut d'abord chercher à comprendre et analyser l'utilité que va apporter le module. Il faut donc définir le besoin et la fonctionnalité implémentée par l'installation de ce module.

Une fois le besoin défini, la première étape est de vérifier qu'il n'existe pas déjà un module de contribution qui implémente notre fonctionnalité.

Il ne faut pas oublier la définition même du mot “module”, qui par essence signifie la réalisation de fonctionnalités séparées, encapsulées et pouvant être retirées à tout moment.

 

  1. Comment organiser ces modules ?

On se pose souvent la question : Est-ce que je devrais pas grouper/dégrouper tel ou tel module ? Est-ce qu'il faut limiter le nombre de modules quitte à en faire un seul qu'on appellerait "main"/"master" ?

De mon avis il y a un entre deux, effectivement il faut éviter d'avoir 30/40/50 modules customs car la maintenabilité des modules peut être potentiellement plus longue et plus complexe de part le travail qui est (par exemple) nécessaire lors des montées de version Drupal.

La structure que j'aime adopter lorsque je construis un projet Drupal est la suivante : Un module par type de contenu (si des développements customs sont nécessaires sur ce type de contenu) puis un module par "grosse" fonctionnalité (exemple : import / export / altération du ckeditor / extensions twig).

 

  1. Création d’un module

Je préconise de toujours utiliser la ligne de commande

vendor/bin/drush generate

pour générer le module (et autres classes également d'ailleurs).

Même recommandation ensuite pour la création des services / controllers / forms, ça permet de développer plus rapidement et plus efficacement sans omettre des notations que l'on pourrait oublier en écrivant "from scratch".

 

  1. JS / CSS / templates au sein d’un module

Lorsqu’on écrit un module, on peut avoir besoin de créer une template spécifique et lui associer du JS / CSS. En pratique, la template fonctionnerait en étant créé et disposé au niveau du thème mais il est préférable de la mettre au niveau du module à partir du moment où son utilisation est propre aux développements du module.

La création d’une librairie se fait via le fichier nom_du_module.librairies.yml.

On crée ensuite notre template et on ajoute le hook_theme à notre module pour spécifier comment devra être rendu un render array avec le thème en question (ici “custom_article_element”).

On ajoute ensuite à notre template le hook permettant d’attacher notre librairie précédemment créée à notre template.

 

  1. Le fichier .module

La fichier .module regroupe l’utilisation des différents hooks Drupal nécessaires à nos développements. Il est important dans la réalisation d’un module de toujours garder à l’esprit l’idée modulaire, ce qui signifie que les hooks renseignées dans notre module doivent toujours rester en lien avec l’idée de fonctionnalité apportée par celui-ci.

Avant d’utiliser un hook, il est important de cibler l’entité que l’on cherche à altérer et de comprendre le moment visé par notre hook. Il existe un grand nombre de hook qui font plus ou moins la même chose mais à des moments différents, exemple : avant le build, pendant le build, avant le rendu, après le rendu...

La documentation Drupal permet d’aller lire et comprendre le hook que l’on va utiliser avant de l’écrire : https://api.drupal.org/api/drupal.

Lorsque l’on développe des fonctionnalités plus complexes, on se retrouve régulièrement à utiliser un nombre de hooks plus élevés et plus fournis, ce qui peut vite mener à un fichier .module très volumineux. La lisibilité et la maintenabilité sont alors vite menacées. 

Au fur et à mesure de mes développements je me suis calqué sur la façon de faire apporté par le module Devel puis étoffé, que je vais détaillée :

 

L’idée est de séparer les fonctionnalités en services, chacun avec une tâche spécifique et manipulant l’entité d’une certaine façon. De cette façon, on va instancier différents services via le service Drupal class_resolver.

Ainsi on va créer au sein de notre module custom Handler(s), Manager(s) et Helper(s) : 

  • Helper : les helpers contiendront les variables de configuration statiques propres à nos fonctionnalités (par exemple l’id technique de notre type de contenu, pour un preprocess).
  • Manager : les managers contiendront la logique “entité” traduisant toutes les méthodes nécessaires pour la manipulation des entités concernées (par exemple requêtes, création, suppression).
  • Handler : les handlers contiendront la logique applicative de notre fonctionnalité, faisant appel aux managers et handlers nécessaires.

[code .module / helper / manager / handler]

 

  1. Documentations

Lors de l’écriture d’un module, il est nécessaire d’écrire le fonctionnement associé à notre module, je recommande de réaliser cette étape quasiment au début du développement de notre module, ça permet en tant que développeur de poser à plat l’idée de la fonctionnalité et parfois de se rendre compte de potentielles failles liées à sa réalisation.

La documentation du module permet également à un futur développeur (ou la reprise de développements longtemps après) de mieux comprendre l’idée du module au moment de sa réalisation.