Vous êtes ici

Premier module Hello World : Créer une nouvelle page

Vous avez 30 secondes ?
S'abonner au flux d'actualités
Rubrique: 
Technique
Difficultée: 
Facile
Dans le tutoriel précédent, nous déclarions notre module Hello World auprès de Drupal 8. Nous allons maintenant créer une nouvelle page sur notre site Drupal, à l'adresse <mon_site.com>/hello_world. Pour cela, nous devons créer une nouvelle route (via les nouveaux mécanismes Symphony 2 inclus dans Drupal 8). Une route est un concept Symphony associé au composant routing (routage). Il permet de créer des "URL créatives", c'est à dire des URLs comme /blog, /mon-article ou encore /hello_world et de leur associer un contrôleur qui va permettre de construire le contenu de cette page. Là encore nous verrons tout en détail dans un prochain article. Le but ici est de simplement présenter les principes de bases de la création de modules Drupal. Dans ce paragraphe nous allons voir les points suivants :
  • nous devons créer des configurations via des fichiers YML dont les noms sont des conventions à retenir.
  • nous devons utiliser des concepts de POO (Programmation Orientée Objet) et parfois étendre des classes de Drupal Core.
  • nous devons utiliser un modèle MVC (Modèle / Vue / Controller) pour gérer nos pages (nous verrons ici le controller).

Création d'une nouvelle route

Commençons par créer notre nouvelle route. Celle-ci définiera une page dont l'URL sera /hello_world et appelant un controleur que nous définierons ensuite et dont le nom sera HelloWorldController. Symphony 2 permet de définir notre route de diverses manières : YML, XML ou PHP. La convention Drupal quant à elle, impose de créer la route dans un fichier $module.routing.yml et placé à la racine de l'arborescence de notre module (donc directement dans le dossier $module). Vous remarquerez l'usage de $module pour désigner le nom machine du module dans la documentation.
  • De la même manière que pour le fichier .info.yml, créez un fichier hello_world.routing.yml :
    • Effectuez un clic-droit sur le nom de votre projet: New -> File.
    • Créez un nouveau fichier hello_word.routing.yml.
  • Entrez le code suivant :
hello_world.hello_world:
  path: '/hello_world'
  defaults:
    _controller: 'Drupal\hello_world\Controller\HelloWorldController::helloWorld'
    _title: 'Hello World'
  requirements:
    _permission: 'access content'
  • La première ligne définit la clef de la route. Nous verrons par la suite que nous pourrons créer des liens dans les menus de Drupal et que ces liens devront se référer à une route. C'est ce nom là qui définit notre route. Ici, j'ai choisi le nom hello_world.hello_world. Cela peut paraître bizarre, mais il faut retenir une chose : le nom de la route doit être unique. Autrement dit, il faut être sûr que ni Drupal, ni aucun autre module développé par n'importe quel développeur n'aura de route du même nom. Pour éviter un maximum les conflits, la convention est de choisir le nom de votre route comme ceci : nom_du_module.nom_de_la_page, où le nom_de_la_page représente ce que la page va faire. Par exemple, pour créer les formulaires d'enregistrement d'un nouvel utilisateur ou encore celui permettant à l'utilisateur de se loguer, le module user créera les routes : user.register et user.login dans son fichier user.routing.yml.
  • La ligne path permet de définir l'URL de la page que nous souhaitons créer. Ici, /hello_world. Ce chemin doit commencer par un slash et ne pas dépasser neuf niveaux.
  • La ligne defaults définit les données par défaut de cette route. Nous développerons les différentes possibilités dans un prochain article. Ici, nous avons utilisé les clefs suivantes :
    • _controller : pour définir le contrôleur chargé de créer le contenu de cette page.
    • _title : pour donner un titre à notre page.
D'autres données peuvent être utilisées. Par exemple la route user.login (définie par le module user) utilise la clef _form pour définir non pas une route se référent à une page avec un contenu, mais plutôt à un formulaire. Celui-ci s'affichera sur la page d'URL définie via la ligne path juste au dessus, mais pourra aussi être réutilisé par la suite dans un block.
  • La ligne requirements définit les pré-requis que l'utilisateur de votre site doit avoir pour utiliser votre route. Dans notre cas, le pré-requis est d'avoir la permissioninfo-icon "access content" (définie par le module core node), c'est à dire le droit d'accès au contenu de notre site (qui pourrait être refusé aux utilisateurs non enregistrés). C'est le sens de la ligne dont la clef est _permission. Nous verrons la aussi dans un prochain article les différents pré-requis possibles, ainsi que comment créer nos propres permissions.
Nous venons de déclarer une nouvelle route pour créer une page dont l'URL est /hello_world et son contrôleur est HelloWorldControlleur. Pour l'instant, ce contrôleur n'existe pas. Si vous vous rendez sur la page /hello_world de votre site, vous n'aurez plus désormais une belle erreur "Cette page n'existe pas" car nous venons de la créer. A la place, vous aurez l'erreur suivante :
 

Création du contrôleur de la page

Durant la déclaration de la route précédente, nous avons délégué la création du contenu de la page à la méthode helloWorld du contrôleur HelloWorldController. C'est ce que nous retrouvions à la ligne :

_controller: 'Drupal\hello_world\Controller\HelloWorldController::helloWorld'

Les utilisateurs de Symphony reconnaîtront ici cette ligne mais noterons quelques différences : la convention Symphony de nommage du contrôleur Bundle:Class:Method est quelque peu différente. Dans Drupal, la convention est la suivante : la clef utilisée sera la plupart du temps _controller lorsque le contrôleur de la page renverra un contenu destiné à être rendu simplement dans un layout HTML. Nous verrons d'autres clefs dans un prochain article.
Le nom du contrôleur pointé par la clef _controller doit suivre la convention suivante :

_controller: \Drupal\nom_du_module\Controller\MaClasseController::maMethode

Notez que le chemin du contrôleur doit commencer par un slash. Notez également que le contrôleur sera placé dans un fichier nommé MaClasseController.php placée dans les répertoires src > Controller
eux-mêmes placés à la racine de votre module.
 
Dans le cadre d'un module, le namespace Drupal\nom_du_module est mappé par convention sur un dossier nommé src placé à la racine de votre module. Votre fichier MaClassController.php sera donc dans :
modules/nom_du_module/src/Controller/MaClasseController.php
 
Le contrôleur est une classe de nom MaClasseController (remarquez que la première lettre de chaque mot est en majuscule, y compris la première et que le nom se termine par Controller). Enfin la fonction lancée par la route est la méthode maMethode présente dans la classe de mon contrôleur. On notera cette fois la présence d'une majuscule à chaque mot, sauf la première lettre.
 
Si vous avez bien suivi, un module nommé myModule est donc placé dans un dossier nommé également myModule dans le répertoire modules de l'installation Drupal. Un de ses contrôleurs appelé FooBarController sera dans un fichier php stocké ici (relativement à la racine de Drupal) :
modules/myModule/src/Controller/FooBarController.php
Cette convention de nommage du chemin est appelé PSR-4.
 
Bon, trêve de blabla, créons notre contrôleur ! Commençons par créer l'ensemble des sous-dossiers src/Controller dans notre module. Pour cela :
  • Effectuez un clic-droit sur le projet de notre module, puis New -> Folder.
Ajouter de nouveaux dossiers dans notre module.
  • Entrez le nom des différents fichiers de la structure à créer :
    • Folder name : src/Controller
  • Cliquez sur Finish.
Créer la hiérarchie des dossiers pour le contrôleur.
  • Créez un fichier HelloWorldController.php. Normalement, vous devriez commencer à retenir comment faire !
    • Effectuez un clic-droit sur le projet de votre module, puis New -> PHP File.
    • Entrez le nom du fichier, File Name = HelloWorldController.php.
    • Cliquez sur Finish.
Le fichier du nouveau contrôleur est désormais crée.
  • Entrez désormais le code suivant :
<?php

/**
 * @file
 * Contains \Drupal\hello_world\Controller\HelloWorldController.
 */

namespace Drupal\hello_world\Controller;
use Drupal\Core\Controller\ControllerBase;

/**
 * Controller routines for hello_world module routes.
 */

class HelloWorldController extends ControllerBase {

 /**
  * Return the 'Hello World' page.
  *
  * @return string
  *   A render array containing our 'Hello World' page content.
  */

  public function helloWorld() {
    $output = array();
   
    $output['hello_world'] = array(
      '#markup' => $this->t('Hello World!'),
    );
    return $output;
  }
}

Le code ci-dessus est du PHP Objet relativement basique. Je vous ferais donc grâce de l'explication du fonctionnement des espaces de nommage et donc des lignes namespace et use. L'important dans ce code est de remarquer que nous définissons bien notre contrôleur : la classe HelloWorldController. Celle-ci implémente la classe abstraite ControllerBase. Cette interface définit un petit contrôleur basique ainsi que quelques méthodes utiles. Par exemple :
  • l() : pour créer un lien vers une route existante.
  • t() : pour traduire une chaîne de caractère dans la langue de l'utilisateur.
  • currentUser() : pour récupérer un objet représentant l'utilisateur visitant la page.
  • ...
Il est conseillé de réserver l'usage de ce contrôleur basique ControllerBase à des pages triviales, c'est à dire à des pages dont le contenu ne change pas selon l'utilisateur, par exemple un formulaire, la page de configuration de votre module, etc.. Les pages à la logique plus complexe où il sera intéressant d'adjoindre des tests unitaires préféreront l'interface : ContainerInjectionInterface.
 
Un peu de technique :
Bien que cela fonctionne techniquement, ce serait une erreur de ne pas hériter d'un contrôleur fourni par Drupal, comme par exemple ControllerBase. L'une des raisons est que l'usage de la fonction t() référencerait la fonction globale t() définie dans bootstrap.inc rendant difficile les unit tests de votre classe (à cause de dépendances globales). En utilisant $this->t(), nous faisons référence ici à l'implémentation de t() fournie par ControllerBase, elle-même basé sur l'usage d'un TranslationManager local.
 
Rappelez-vous la ligne suivante du fichier hello_world.routing.yml :

_controller: 'Drupal\hello_world\Controller\HelloWorldController::helloWorld'

Nous avons vu que cette ligne définit la cible de la route dans le contrôleur. En l’occurrence la méthode helloWorld(). C'est cette méthode publique que l'on retrouve définie dans notre contrôleur. A l'intérieur de cette méthode, l'objet $output est appelé "Render Array" ou "Renderable Array". Il s'agit d'une structure définissant un objet destiné à être rendu (affiché sur le site) en HTML. La clef #markup définit le plus simple élément pouvant être affiché : une chaîne de caractères simple, rendue dans une <div> ou un <p>. Les propriétés à utiliser sont spécifiques à chaque type d'éléments pouvant être rendus (champ de texte, image, case à cocher, etc...) et chaque module peut ajouter éléments et propriétés. Créer une liste exhaustive est donc une tâche ardue voir impossible. A ma connaissance il n'y a pas de documentation complète des propriétés des Render Array. Toutefois, il est à noter que les formulaires sont rendus (transformés en code HTML affichable) par le même mécanisme. Vous pouvez donc utiliser la documentation Form API Reference pour trouver un maximum d'éléments et propriétés utiles. Enfin, vous pouvez lire la documentation sur les Renderable Arrays qui définie un certain nombre de propriétés supplémentaires, notamment pour l'usage des caches, thèmes et autres.
  • Activez à présent votre module (si ce n'est pas encore fait) ou videz le cache de votre site (si le module est déjà activé).
Rendez-vous à la page /hello_world de votre site ! Une (presque) magnifique page apparaît, avec votre titre et votre texte. C'est pas beau la vie ?!
 
Votre première page Hello World !
 
En résumé...
 
Basiquement, une route est un lien logique entre l'URL entrée dans le navigateur et la page (ou l'élément) devant s'afficher à l'écran. Cette route est définie dans un fichier $module.routing.yml placée à la racine du module.
 
Le Controller est la classe en charge de la création de l'élément en destination de route. Nous verrons plus tard pourquoi nous l'appelons Controller. Cette classe est placée dans un fichier nommé FooBarController.php, lui même inséré dans la structure de répertoire suivante :
/src/Controller.
Fichier(s) joint(s): 
Notation: 
Average: 4.3 (6 votes)
Vous avez aimé: 

Commentaires

Erratum il s'agit bien de la page /hello_word dans mon commentaire précédent.
 
Par ailleurs dans le code, la classe de base ControllerBase est soulignée en jaune et lorsqu'on passe le curseur dessus le message "The type ControllerBase cannot be resolved" s'affiche. L'instruction use est elle aussi soulignée en jaune avec le message "The type lib\Drupal\Core\Controller\ControllerBase cannot be resolved or is empty"
 
7
This post is useful!
This post is useless!
-2
Loïc GR

Bonjour,
 
Je viens de réessayer avec une installation propre de Drupal8-alpha11 et la page /hello_world est accessible sans problème. Avez-vous bien tout suivi de l'article et créer les deux fichiers hello_world.routing.yml et lib/Drupal/hello_world/Controller/HelloWorldController.php sans rien modifier du code que je propose dans le tutoriel ? Question qui peut paraître triviale : vous avez bien activé le module nouvellement crée ?!
 
Pour ce qui est du ControllerBase, je suppose que vous n'avez pas suivi la procédure de création d'un nouveau module via Eclipse ici :
En particulier l'ajout de Drupal 8 dans l'include Path du projet.
C'est toujours possible maintenant via un clic-droit sur le projet puis Properties et enfin en suivant l'image du tuto :
8
This post is useful!
This post is useless!
-2

Bonjour,
J'ai tout repris depuis le début, pas à pas en lisant et relisant maintes fois le tuto et j'arrive toujours au même résultat. Tout y est, les 3 fichiers hello_word avec ce qui va bien dedans, l'include de Drupal, etc. et pourtant ça ne marche pas. Le host virtuel debug.local fonctionne correctement, l'installation et l'initialisation de Drupal s'est faite normalement. Curieusement je n'ai plus les soulignés en jaune dans le code PHP après les avoir supprimés et retapés manuellement (?!!). Tout semble bien fonctionner sauf l'affichage de la page Hello Word.
 
Est-ce que Drupal a un fichier de log quelque part qui pourrait me renseigner ?
 
Cordialement (et bravo pour votre site)
Loïc
6
This post is useful!
This post is useless!
-3
Loïc GR

Vous devriez avoir 4 fichiers :
hello_world.module
hello_world.info.yml
hello_world.routing.yml
lib/Drupal/hello_world/Controller/HelloWorldController.php
- Vérifiez que vous avez bien activé le module sur la page des modules.
- Vous pouvez vérifier les logs Drupal au cas où sur : admin/reports/dblog
- Vous pouvez activer le module Syslog pour plus de logs.
6
This post is useful!
This post is useless!
-2

Merci de votre retour ! C'est long à lire et encore plus à écrire !
 
Pour l'instant, il n'y a pas de newsletter mais j'en ajouterais peut-être une par la suite. Il est toutefois possible d'être prévenu des nouveaux tutoriaux en choisissant les options "Posts de type Tutoriel" ou "Tout le site" dans les abonnements, sur la colonne de droite sur chaque tutoriel. Si tout se passe bien, vous devriez pouvoir recevoir un mail lors de la publication d'un nouveau tuto.
Sinon, il est également possible de simplement faire un tour sur la page d'accueil : les nouveaux contenus y sont listés avec la date et l'heure de publication.
This post is useful!
This post is useless!

L'article à été mis à jour pour refléter les derniers changements issus du choix de la convention du PSR-4 :
 
@Loïc : essayez voir avec cette convention désormais, votre module devrait à nouveau fonctionner : déplacez simplement le controller dans src/Controller au lieu de lib/Drupal/....
This post is useful!
This post is useless!
Portrait de Ralf

Re bonjour Dominique, J'ai également un petit problème de route, Drupal me renvoie un erreur 404. Pourtant le module est bien sélectionné (d'ailleurs je ne peux pas le désélectionner...), le controlleur est en place, ce qui me fait un total de 3 fichiers sans compter les répertoires. Si j'ai bien compris le fichier .module n'est pas obligatoire, c'est bien ça ? Merci ;)
This post is useful!
This post is useless!

Bonjour,
Je viens de retester le tuto avec la dernière version beta disponible pour Drupal8: aucun soucis.Je vous confirme aussi que le fichier .module est optionnel.
J'ai zippé le code et je l'ai ajouté à la fin de l'article afin que vous puissiez comparer s'il manque quelque chose.
Notez que si vous avez activé votre module lors du tuto précédent, il vous faudra alors vider le cache ( /admin/config/development/performance ) afin que la déclaration des routes soit prise en compte, ce qui pourrait expliquer votre soucis.
1
This post is useful!
This post is useless!

Super, merci pour la réponse !
Effectivement, le cache de Drupal posait problème... En avant pour la suite de ce tutoriel, merci les tutoriels sur les CMS se font rare dans notre langue ;)
This post is useful!
This post is useless!
Ralf Saenen Etudiant à la Facultés des Sciences de Montpellier Master 2 AIGLE http://www.saenen.fr

Bonjour,
 
J'ai beau faire, j'ai toujours "Page non trouvé". Pour être sûr de bien faire, j'ai supprimé tout ce que j'avais fait, désinstallé le module, puis j'ai téléchargé le votre code et installé votre module. Et j'ai le même résultat : Page non trouvé.
 
Il doit y avoir quelque chose qui cloche au niveau de la config, mais quoi ?
 
Si quelqu'un a une idée ...
Cordialement.
This post is useful!
This post is useless!
Loïc GR

Bonjour loic,
Essayez cette manip:
Créez un nouvel utilisateur avec juste le role authentifié et vérifiez qu'il soit actif. Videz le cache de Drupal.Passez en mode navigation privé sur le navigateur , connectez vous à votre installation de drupal avec le nouvel utilisateur "auhentifié" et lancez votre url qui affiche "Page non trouvée".
 
Dites-nous si le contenu s'affiche...
 
Cldt.
1
This post is useful!
This post is useless!

Bonjour,
Je viens à nouveau de vérifier le dernier code du zip de cet article. Il fonctionne très bien. Je suggère, tout comme dx dans son commentaire, que vous :
- supprimez le module actuel
- vider le cache
- installer le module du zip de ce tuto
- vider le cache
- rendez-vous sur /hello_world
 
Attention: suivant comment vous avez configurer votre install local, il se peut que l'URL soit de l'une des formes suivantes :
This post is useful!
This post is useless!

Bonsoir,
 
Je viens de suivre pas à pas au debugger ce que fait Drupal. J'ai pu capter les messages d'erreurs que voici :
GET /D800b11/hello_word?XDEBUG_SESSION_START=19152 HTTP/1.1
Accept:          text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.5
Authorization:   
Connection:      keep-alive
Cookie:          SESSb7ad567477c83756aab9a542b2be04f7=ptunobWKmH8HJw7hrOpArPMl-EZaCOWusUl54HQhFAc; SESS76c7c41d86289e5e99e0e28f24baa862=nHymq9STGhr4vUC1CkDg-xXglvu9uvfYmmD7i15vI1w; XDEBUG_SESSION=19152
Host:            sandbox
User-Agent:      Mozilla/5.0 (Windows NT 6.3; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0
No route found for "GET /hello_word"
 
Visiblement Drupal ne trouve pas la route. Je ne sais même pas s'il voit le fichier hello_word.routing.yml. J'ai le sentiment que non. Aucun secours du coté de la log drupal qui est plus que sommaire puisqu'elle se contente d'afficher "Page non trouvée".
 
 
This post is useful!
This post is useless!
Loïc GR

Salut et merci pour ces tuto qui permettent de remettre le pied à l'étrier ;)
 
J'ai l'impression d'être dans le même cas de figure que Loic, c'est à dire que je n'arrive pas à accéder à la page hello_world.
 
Tout d'abord ma config, j'utilise la version 8.0.0, je n'ai pas suivi le tuto sur la création de l'espace de travail car j'ai l'habitude de travailler avec netbeans (mais je ne pense pas que cela pose problème) et dernière chose je travaille sans le net, en local avec Xampp.
 
J'ai quand même quelques questions sur le tuto sur des points que je ne comprend pas :
 Tout d'abords au niveau de l'endroit où créer le dossier Controller, dans la route tu dis qu'il faut le créer dans la racine du module mais lors de la création tu le crées dans le dossier /src (peut-être une modification faite durant la beta) et du coup que choisir ? racine ou /src, si on choisit le dossier src il faut modifier la route et les différents liens dans le Controller.php.
 
Autre question, en tout début de chaque lien il y a le dossier drupal, et dans mon projet je n'ai pas de dossier drupal. Est-ce que Drupal a une variable qui permet de modifier en internet les chemins Drupal/ en [dossier racine]/modules/ ? où est-ce qu'il faut que je modifie les chemins du tuto ?
 
Merci pour tes éclaircissements 
 
Olo
This post is useful!
This post is useless!

Je me répond à moi même, c'est sale, mais si ça peut permettre à d'autre de corriger leur problème...
 
En fait j'ai trouvé d'où provenait mon problème, en fait j'avais un module que j'avais commencé à créer qui contenait des erreurs et lorsque je vidais le cache celui-ci ne se vidait pas à cause du module en erreur.
Après avoir supprimé ce dernier j'ai réussi à voir la page et à continuer le tuto \o/
This post is useful!
This post is useless!

Hello !
Donc en conclusion du message précécent, le tuto est bien correct et vous avez pu répondre à vos propres questions ?!
Dans le cadre violet il a un petit rappel: ça peut effectivement sembler surprenant mais c'est la convention dite PSR-4:
le module se place dans /modules puis dans un dossier au nom du module (exempe: myModule). Du coup, un controller est par convention dans le dossier /modules/myModule/src/Controller mais son namespace doit être:

namespace Drupal\myModule\Controller

This post is useful!
This post is useless!
Portrait de Corentin

Bonjour, Merci pour la réactivité de réponse ! Je suis sur Ubuntu, j'utilise apache et mysql. J'ai sauté la première étape de Git ainsi que la partie sur XDebug... J'ai configuré Eclipse, j'arrive à obtenir le module dans la liste des extensions. Dans eclipse mon projet "drupal" a quelques erreur dans le dossier vendor je ne sais pas si ca pourrait avoir un rapport. Sinon je fais peut être une erreur dans l'arborescence... Corentin
This post is useful!
This post is useless!
Portrait de Corentin

Derniere précision, j'ai cette erreur sur Drupal : The trusted_host_patterns setting is not configured in settings.php. This can lead to security vulnerabilities. It is highly recommended that you configure this. See Protecting against HTTP HOST Header attacks for more information. Je regarde à quoi cela correspond.
This post is useful!
This post is useless!

Bonjour,
Je suis un peu perdu ^^ Je ne parle pas d'un dossier "vendor" dans mon tuto ! Egalement le répertoire à utiliser pour le Controller n'est pas interchangeabe (lib / src / etc..). C'est une convention (PSR-4) d'autoloading qu'il faut respecter. Le Controller est dans un dossier "src", mais la méthode du contrôleur est pointé via son namespace dans le routing.yml: 'Drupal\hello_world\Controller\HelloWorldController::helloWorld'.
Je vous encourage à télécharger et essayer mon exemple au préalable pour bien comprendre de quoi il retourne à la base, pour personaliser ensuite.
This post is useful!
This post is useless!
Portrait de Corentin

D'accord j'ai bien compris. j'ai téléchargé l'exemple, mis le dossier hello_world dans le dossier modules/ puis j'ai testé l'URL http://localhost/drupal/hello_world : Page non trouvée :( Cela peut venir d'une erreur de configuration au niveau d'apache ou je ne sais quoi ? Plutot que du code en lui même ?
This post is useful!
This post is useless!
Portrait de Corentin

J'ai refais le module via eclipse, je l'installe mais la page est non trouvée. J'ai donc désinstallé puis réinstallé, et au moment de vider le cache, le navigateur affiche une page vierge, j'ai donc regardé les logs http://localhost/drupal/admin/reports/dblog. J'ai cette erreur : User warning : The following module is missing from the file system: chad dans drupal_get_filename() (ligne 231 de /var/www/html/drupal/core/includes/bootstrap.inc).
This post is useful!
This post is useless!