Aller au contenu principal


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".

information complémentaire disponible en cliquant ici

Dans l'univers Drupal, le nom d'un module doit être unique. Il s'écrit en minuscule, le seul caractère spécial autorisé est _ (-underscore-). 

 

Dernière MAJ :
28 nov 2024
Billet créé le :
21 nov 2020

A la racine du dossier "dh_like", nous trouverons tous ou quelques-uns de ces fichiers :

  1. dh_like.info.yml : déclaration du module (obligatoire) ;

  2. dh_like.links.menu.yml : déclaration des liens de menu ;

  3. dh_like.routing.yml : déclaration des routes ;

  4. dh_libraries.yml : déclaration des bibliothèques (JS et/ou CSS) ;

  5. dh_like.permissions.yml : déclaration des droits d'accès ;

  6. dh_like.module : définition du hook_theme ;

  7. 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".

information complémentaire disponible en cliquant ici Quels langages pour le codage ?

Les 5 premiers fichiers portent l'extension ".yml"  et respectent la syntaxe YAML. Ils définissent l'ensemble des hiérarchies qui organisent le fonctionnement du module.      
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 ...

information complémentaire disponible en cliquant ici Génération automatisée du squelette d'un module

 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.

Cette image illustre les connexions existant entre ces divers fichiers.

Cliquer pour agrandir l'image

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.  

  1.  Le cadre "1" indique l'adresse de la page d’administration des modules pour mon site de développement : https://localhost/admins/module :

  2. la clé "package" ayant "custom" pour valeur se traduit par la présentation du module dans l'item "personnalisé" (cadre 2)  ;

  3. 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   

Cliquez pour agrandir l'image

2. Le fichier "dh_like.links.menu.yml"

  • 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

Cliquez pour agrandir l'image

information complémentaire disponible en cliquant ici

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 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

Le code ci-dessous est celui du fichier "dh_like.links.action.yml". Il ajoute les boutons "Add link" et "Add menu" à la page "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 ;

  • 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 "." .
       

information complémentaire disponible en cliquant ici

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.

Cliquer pour agrandir l'image

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.

information complémentaire disponible en cliquant ici

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.

en savoir encore plus ...

 

Il est également possible de contrôler les accès dans le code. 
Par exemple :
if (\Drupal::currentUser()->hasPermission('dh_like, skip')) {} else {}
permet de contrôler l'accès de l'utilisateur actuel.

 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."

Cliquer pour agrandir l'image

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 }}.

Cliquer pour agrandir l'image

 

 

Notons que :
  • 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 :

 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.

Une version beaucoup plus développée est décrite dans le billet "administrer un module" du site

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>

Cliquer pour agrandir l'image

Cliquez sur le bouton pour copier le code 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;
   }