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 et/ou CSS) ;
dh_like.permissions.yml : déclaration des droits d'accès ;
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".
Quels langages pour le codage ?
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-dessus.
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"
La présence de ce fichier d'extension "info.yml" est obligatoire dans un module. 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>) ;
package : (recommandé à Custom). Permet de grouper les modules perso sous l'item "personnalisé" dans la page d'administration des modules ;
type : (obligatoire) valeurs possibles : module, theme ou profile ;
core_version_requirement : (obligatoire ) : ^10 |^11 (versions de drupal supportées par ce module) ;
version : (optionnel) ;
configure : (optionnel) spécifie 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 dans la page "/admin/modules", l'affichage illustré dans l'image ci_desous.
Le cadre "1" indique l'adresse de la page d’administration des modules pour mon site de développement : https://localhost/admins/module :
la clé "package" ayant "custom" pour valeur se traduit par la présentation du module dans l'item "personnalisé" (cadre 2) ;
- les valeurs des clés "name", "description", "version", etc sont affichées dans le cadre 3.
name: dh_like
type: module
package: Custom
description: Fourni un compteur de like.
core_version_requirement: ^10 || ^11
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 :
Le code ci-dessous est celui du fichier "dh_like.links.task.yml". Il ajoute les onglets 1 et 2 (hiérarchie "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 ici par dh_like ;
ModuleController doit être remplacé par le nom de la classe (au sens namespaced class), donc ici par DhlikeListController ;
methode doit être remplacé par le nom de la méthode (function) créée dans la classe déclarée ci-dessus, donc build().
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 "build()" du contrôleur "DhlikeListeController" 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\DhlikeListeController:: build'
_title: 'Likes'
requirements:
_permission: 'access content','administrer dh_like'
La classe "DhlikeListeController" est définie dans le fichier "DhlikeListeController.php". Elle étend la classe abstraite "ControllerBase" et produit, à travers sa fonction "build()", la page "/admin/config/services/dh_like" illustrée par l'image ci-joint. 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és :
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 renvoie le contexte d'affichage du module (grossiérement dit : quelles variables sont utilisées et quel(s) template(s) (gabarit) 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 dhlike_theme($existing, $type, $theme, $path) {
return [
'dhlike_block' => [
'variables' => [
'data' => [],
'sousTitre' => [],
'vote' => [0,0,0],
'config' => [],
],
'template' => 'dhlike-block',
],
'dhlike_liste' => [
'variables' => [
'data' => [],
],
'template' => 'dhlike-liste',
],
];
}
Avec le code ci-dessus et comme l'illustre l'image jointe, deux "templates" sont chargés de l'affichage du module :
le template "dh-like-block.html.twig" ;
le template "dh-like-liste.html.twig".
Dans ces 2 templates, pour afficher le contenu de la variable 'data', il faut naturellement utiliser la syntaxe twig : {{ data }}.
- les extensions sont implicites ;
- 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'=>[],'sousTitre' => [],'vote' => [0,0,0],'config' => []] permet de récupérer les valeurs des clés du "tableau de rendu" (ie :"renderable array") produit par la fonction "build" de la classe "DhlikeBlock" (fichier DhlikeBlock.php) responsable de la génération du bloc dhLike. Le billet "le bloc "likes" du site en décrit le fonctionnement.
On y ajoute si nécessaire une valeur par défaut. Pour récupérer également un attribut #var de ce "renderable array", on utiliserait : 'variables' => ['data'=>[], 'var'=>[], ] .
Relire l'article "Les gabarits" du site, pour revoir comment "passer" en mode "debug".
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 "DhlikeListeController.php", cf : dh_like.routing.yml vu en §3) ;
"plugin" qui contient par exemple :
Analyse de DhlikeListeController.php :
<?php
/**
* @file
* Contains \Drupal\dh_like\Controller\DhlikeListeController.
*/
namespace Drupal\dh_like\Controller;
use Drupal\Core\Controller\ControllerBase;
class DhlikeListeController extends ControllerBase {
public function build() {
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 "DhlikeListeController" et sa méthode build() 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 build() 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>
#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;
}