Autopsie d'un module
Dans l'univers Drupal, un module s'appuie sur une arborescence de fichiers très précise dont la racine porte le nom du module et qui se trouve, pour les modules certifiés Drupal, dans le dossier "modules/contrib" de votre site et sinon, dans le dossier "module/custom" . Pour la clarté du billet, je considérerai que l'on analyse mon module personnel "dh_like" qui affiche sur certaines pages, le bloc "Aimez-vous cette page ?"
L'arborescence débute donc par un dossier nommé "dh_like" situé dans le dossier "modules/custom".

A la racine du dossier "dh_like", nous trouverons tous ou quelques-uns de ces fichiers :
- dh_like.info.yml : déclaration du module (obligatoire) ;
- dh_like.links.menu.yml : déclaration des liens de menu ;
- dh_like.routing.yml : déclaration des routes ;
- dh_libraries.yml : déclaration des bibliothèques JS ou CSS ;
- dh_like.permissions.yml : déclaration des droits ;
- dh_like.module : définition du hook_theme ;
- dh_like.install: définition du hook-shema ;
Les fichiers numérotés 1 à 6 sont étudiés dans les paragraphes ci-dessous. Le fichier 7 est présenté dans le billet "gérer la table likes"

Les fichiers d'extension ".module" et ".install" sont écrits en PHP.
Dans ce dossier "dh_like", nous trouverons également les dossiers :
- "templates" qui contient les fichiers, écrits en YAML, pour les gabarits (modèles) des pages de ce module.
- "src" qui contient des fichiers, écrits en PHP, comme par exemple :
- dans le dossier "form", le code pour les formulaires :
- dans le dossier "controller", le code pour les contrôleurs du module ;
- dans le dossier "plugins", le code pour les blocs ...


Notez qu'il est possible de créer la structure complète d'un module grâce à la commande "drush gen module" comme l'illustre l'image ci-contre.
Dans cette illustration, la structure du module "learning" est créée automatiquement avec ses 7 fichiers.
Sont créés également, les fichiers :
- LearningController.php, placé dans le répertoire "src/Controller" : un code exemple pour créer un contrôleur définissant les actions à mener par les routes ;
- ExempleBlock.php, placé dans le répertoire "src/Plugin/Block" : un code exemple pour la réalisation d'un bloc ;
- SettingForm, placé dans le répertoire "src/Form" : un code exemple pour créer un formulaire permettant de stocker un élément de configuration en base de données.
1. le fichier "dh_like.info.yml"
Ce fichier est le seul à être présent dans tous les modules. Il a pour fonction la déclaration du module à travers la hiérarchie suivante :
- name: (obligatoire) le nom du module tel qu'il apparaitra dans la page d'administration des modules ("admin/modules") ;
- description: (recommandé) de 255 caractère au plus. Ce texte apparait dans cette même page d'administration (il peut comporter un lien <a href=" "> </a>) ;
- type: (obligatoire) valeur possible : module, theme ou profile ;
- core_version_requirement: (obligatoire ) : ^10
- version: (optionnel) ;
- configure: (optionnel) spécifies la route vers la page du formulaire de configuration du module ;
- dependencies: (optionnel) la liste des modules dont dépend le module.
Accès à la documentation officielle.
Exemple :
Le fichier "dh_like.info.yml" dont le code est affiché ci-dessous produit l'affichage illustré dans l'image pour la page "/admin/modules" mis en évidence par le "1" sur l'image ci-dessous.
name: dh_like
type: module
package: Custom
description: Fourni un compteur de like.
core: 10.x
core_version_requirement: ^10
version: 1.0
2. Le fichier "dh_like.links.menu.yml"
Ce fichier a pour fonction la déclaration des liens de menus fournis par le module.
Il contient une hiérarchie pour chaque lien à décrire. Pour respecter la règle de nommage, le nom d'une hiérarchie doit débuter par le nom du module (donc pour mon module par "dh_like."). Elle porte le nom (unique) de la route à décrire. . Elle doit avoir comme clés :
- title: (obligatoire) définit le texte du lien dans le menu ;
- description: (optionnel) le texte de la balise <title> du lien ;
- route_name: (obligatoire) le nom de la route à appeler (cf : .routing.yml) ;
- menu_name: (optionnel) le nom du menu dans lequel sera placé le lien ( par défaut le menu "tools - outils" : /admin/structure/menu/manage/tools) ;
- parent: (optionnel) le nom du lien parent dans le menu ( par défaut la racine) ;
- weight: (optionnel) le rang dans l'ordre d'affichage (par défaut ordre alphabétique)
Accès à la documentation officielle.
Exemple :
Le fichier "dh_like.links.menu.yml" dont le code est affiché ci-dessous produit l'affichage illustré ci-dessous.
"dh_like.home:" est le nom de la hiérarchie présentée.
dh_like.home:
title: 'Dh_Like'
description: 'Configuration du module dh_like'
parent: system.admin_config_services
route_name: dh_like.home
weight: -100

Une lecture attentive de la documentation officielle montre le processus de création d'onglets et de boutons dans la page d'administration du module en créant à la racine du module les fichiers :
- dh_like.links.task.yml pour déclarer les onglets à ajouter ;
- dh_like.links.action.yml pour déclarer les boutons à ajouter ;
Le code ci-dessous est celui du fichier "dh_like.links.task.yml". Il ajoute les onglets1 et 2 (hiérarchies "onglet") à la page "content" (hiérarchie "admin")
admin:
route_name: content
title: 'Information'
base_route: content
onglet1:
route_name: onglet_1
title: 'onglet_A'
base_route: content
onglet2:
route_name: onglet_2
title: 'onglet_B'
base_route: content
link_add:
route_name: add_link
title: 'Add link'
appears_on:
- content
menu_add:
route_name: add_menu
title: 'Add menu'
appears_on:
- content
Il faut évidemment adapter le fichier "dh_like.routing.yml" pour y définir les nouvelles routes crées.
L'image ci-dessous illustre l'effet de ces deux fichiers.
3. Le fichier "dh_like.routing.yml"
Ce fichier a pour fonction la déclaration des "routes" du module. Une route relie un lien ("path") à un "gestionnaire" (contrôleur, formulaire, etc) qui effectue des traitements et affiche un contenu.
Accès à la documentation officielle.
Ce fichier contient une hiérarchie pour chacune des routes à déclarer.
Pour respecter la règle de nommage, le nom de chaque hiérarchie doit débuter par le nom du module. Elle doit avoir comme clés :
- path: URL de la page créée. Ce chemin doit commencer par un slash et ne pas dépasser neuf niveaux ;
- defaults: hiérarchie qui définit les caractéristiques par défaut de la route. Elle indique principalement comment construire le contenu principal (ie : la page) correspondant à cette route. ;
- _controller::method ou _form: ou ... : le nom complet du contrôleur ( au sens modèle - vue - contrôleur ) ou du formulaire ... voir https://www.drupal.org/docs/drupal-apis/routing-system/structure-of-routes
- _title: le titre de la page créée ;
- requirements: hiérarchie qui définit les pré-requis à avoir pour accéder à la page. Cette hiérarchie peut utiliser les clés :
- _permission: le nom de la (des) autorisations d'accès à la page. Les autorisations de base de Drupal peuvent être complétée par un module à travers le fichier d'extension ".permissions.yml" (voir le §5). Ces autorisations se combinent par les opérateurs logiques "+" (ou) et "," (et).
- _role: le nom du (des) rôle(s) autorisés. Ces rôles se combinent également par les opérateurs logiques "+" et "," .

La clé _controller: (resp. _form) doit s'écrire sous la forme 'Drupal\module\Controller\ModuleController::methode' (resp. Drupal\module\Form\ModuleForm) dans laquelle :
- module doit être remplacé par le nom du module (en minuscule), donc par dh_like ;
- ModuleController doit être remplacé par le nom de la classe (au sens namespaced class), donc LikeController ;
- methode doit être remplacé par le nom de la méthode (function) créée dans la classe déclarée ci-dessus, donc content().
Il est possible de passer des paramètres dans la route :
- ils figurent entre accolades dans la clé "path:" (exemple : path: '/dh_like/ongletLike'/{param}') ;
- ils figurent comme paramètres de la fonction de traitement de la route (exemple : content($param)) ;
- ils sont validés dans la clé "requirements:" par exemple pour le paramètre "param" par :
- param: 'article|page|billet' ( "|" est l'opérateur logique "ou") : une liste de valeur
- param: '[a-zA-Z]+ : une expression régulière.
La hiérarchie "dh_like.home:" du fichier "dh_like.routing.yml" produit, par la méthode "content()" du contrôleur "LikeController" l'affichage illustré ci-dessous. Il s'agit d'une version simple de la page d'administration du module "dh_like". Une version plus élaborée est décrite dans le billet "Administrer un module".
dh_like.home:
path: '/admin/config/services/dh_like'
defaults:
_controller: '\Drupal\dh_like\Controller\LikeController::content'
_title: 'Likes'
requirements:
_permission: 'access content','administrer dh_like'
La classe "LikeController" est définie dans le fichier "LikeController.php". Elle étend la classe abstraite ControllerBase. et produit, à travers sa fonction "content()", la page "/admin/config/services/dh_like" illustrée par l'image ci-contre. Ce fichier, qui est situé dans le dossier "src/controller" du module "dh_like", est décrit dans le chapitre 7 de ce billet.
4. Le fichier "dh_libraries.yml"
Ce fichier contient les hiérarchies annonçant les fichiers de style associés au module.
Accès à la documentation officielle.
Dans le code suivant, qui est celui de mon module personnalisé "dh_like", la hiérarchie "custom" est appelée par les "renderable array" des fichiers "like_controller.php" présenté dans le paragraphe 7 et "dh_like_form.php" présenté dans le billet "le bloc like" pour mettre en forme les items produits par le module : page, formulaires...
custom:
version: 1.x
css:
theme:
css/dh_like.css: {}
Le billet ".libraries.yml" présente l'étude du fichier des librairies utilisé par le thème de ce site.
5. Le fichier "dh_like.permissions.yml"
Ce fichier a pour fonction la déclaration des sections à ajouter dans la page "/admin/people/permissions" pour les droits d'accès à ce module.
Il contient une hiérarchie par section à ajouter, chaque hiérarchie porte le nom (unique) de la section à décrire.
Une hiérarchie a pour clé :
- title: (obligatoire) le titre qui sera affiché en tête de section ;
- description: (obligatoire) sera affiché en complément du titre dans la page des permissions ;
- restrict access: (optionnel) ie par défaut FALSE - si TRUE affichage de l'info d'avertissement danger de Drupal ;
Accès à la documentation officielle.

Pour que des sections soient ajoutées dynamiquement (par programmation), il faut déclarer une hiérarchie dont le nom est "permission_callbacks"
qui suit cette syntaxe
permission_callbacks:
- Drupal\module\ModuleClass::méthode
dans laquelle :
- module doit être remplacé par le nom du module
- ModuleClass doit être remplacé par le nom de la classe définie dans ce module (au sens namedclass). Celle ou se trouve la fonction qui effectue le traitement.
- méthode doit être remplacé par le nom de la la méthode (function) qui effectue le traitement et qui est définie dans la classe ci-dessus.

Par exemple :
Ci-dessous figure le code du fichier "dh_like.permissions.yml" et la vue qu'il produit dans la page des droits ("\admin\people\permissions").
administrer dh_like :
title: 'Administer dh_like settings'
description: 'les utilisateurs ayant cette permission administreront les likes.'
restrict access : true
skip:
title: 'Skip dh_like'
description: "Les utilisateurs ayant cette permission n'accederont pas aux likes."
6. Le fichier "dh_like.module"
Ce fichier écrit en PHP génère le contexte d'affichage du module.
Dans le "jargon" de Drupal, il s'agit d'implémenter le hook_thème : une fonction qui doit être nommée "module_theme()", ie pour mon module, le hook_ thème s'appellera donc"dh_like_theme()".
Cette fonction génère le contexte d'affichage du module (grossiérement dit : des variables utilisées et le nom du (des) template(s) (gabarit) qui réalise(nt) l'affichage).
Accès à la documentation officielle.
Ci-dessous, le code du fichier "dh_like.module" de mon module perso "dh_like".
<?php
/**
* Implements hook_theme().
*/
function dh_like_theme() {
return [
'dh_like_block' => [
'variables' => [
'data' => [],
],
],
'dh_like_controller' => [
'variables' => [
'data' => [],
],
// j'impose le nom du template à utiliser
'template' => 'dh-like',
],
];
}
Avec le code ci-dessus, deux "templates" sont chargés de l'affichage du module .
- le template "dh-like-block.html.twig"
- le template "dh-like.html.twig"
Dans ces 2 thèmes, pour afficher le contenu de la variable 'data', il faut utiliser la syntaxe twig : {{ data }}.

- les extensions sont implicites ;
- lorsque le nom du template n'est pas précisé, il est déduit de l'attribut #theme du "renderable array" appelant le "hook_thème". Les "_" (underscores) sont automatiquement remplacés par des "-"(tirets) ;
- Ces fichiers doivent se trouver dans le dossier "templates" à la racine du dossier du module (ici : /modules/dh_like/templates/) .
La ligne de code 'variables' => ['data'=>[] ] permet de récupérer les variables de l'attribut #data du "renderable array" en y ajoutant si nécessaire une valeur par défaut. Pour récupérer également l'attribut #var de ce "renderable array", on utilisera : 'variables' => ['data'=>[], 'var'=>[], ] .
7. Le dossier "src"
Ce dossier contient les fichiers et les dossiers réalisant le module. On y trouve en particulier les dossiers :
- "controller" qui contient les fichiers définissant les contrôleurs (par exemple le fichier "LikeController.php", cf: dh_like.routing.yml vu en §3) ;
- "form" qui contient les fichiers définissant les formulaires (par exemple : le fichier like_form.php);
- "plugin" qui contient par exemple :
Analyse de LikeController.php :
<?php
/**
* @file
* Contains \Drupal\dh_like\Controller\LikeController.
*/
namespace Drupal\dh_like\Controller;
use Drupal\Core\Controller\ControllerBase;
class LikeController extends ControllerBase {
public function content() {
return array(
'#type' => 'markup',
'#markup' => t('La page de likes'),
'#prefix' => '<span class="dh_like_admin"> ',
'#suffix' => '</span>',
'#attached' => [
'library' => [
'dh_like/custom',
],
],
);
}
}
Ce fichier définit la classe "LikeController" et sa méthode content() dont l'objectif est l'affichage de (la traduction de) "La page de likes". D'après le fichier "dh_like.routing.yml", cette page a pour titre "Likes", son adresse est "/admin/config/services/dh_like", elle est illustrée par l'image du $3.
Dans l'univers "Drupal", le tableau renvoyé par la fonction content() s'appelle "renderable array". Lire ces pages https://happyculture.coop/blog/drupal-8-lapi-de-rendu-render-api et https://www.drupal.org/docs/drupal-apis/render-api/render-arrays pour en savoir plus.
8. Le dossier "templates"
Ce dossier contient le(s) modèle(s) d'affichage (templates) pour les pages créées par le module (cf : le fichier "dh_like.module").
Un "template" affiche, en particulier, le contenu (de certaines) des variables déclarées dans les "renderable array" des fonctions du module (voir le §7).
Le fichier "dh-like-block.html.twig", dont le code suit, produit l'affichage illustré ci-dessous. Cet affichage est mis en forme par les styles définis dans la bibliothèque "dh_like.css" attachée à ces "renderable array". Le code de cette bibliothèque figure dans l'onglet ci-dessous.
Une description plus détaillée du bloc des likes est présentée dans le billet " le bloc likes".
<div class="dh_like_block">
<div>
{{ data.like_button }}
</div>
<div>
{{ data.dislike_button }}
</div>
</div>
dans le presse papier
#liked-form-button{
height: 30px;
cursor: pointer;
background: #f0f0f0 url(images/pplus.png) no-repeat center;
border-radius: 5px 5px 5px 5px;
}
#disliked-form-button{
height: 30px;
cursor: pointer;
background: #f0f0f0 url(images/pmoins.png) no-repeat center;
border-radius: 5px 5px 5px 5px;
}
.dh_like_block {
display:flex;
align-items:center;
justify-content: space-evenly;
float: right;
background-color: gainsboro;
margin: 0px 7px 0px 0px;
}
.dh_like_block div{
margin:0px 5px 0px 0px;
}
.dh_like_admin{
display: flex;
justify-content: center;
font-size: x-large;
margin: 2em;
}