Porter son module en D8 - #1 Rendre le module installable

Ceci est le premier article d'une série qui décrira étape par étape la conversion du module Multiple E-mail Addresses à Drupal 8. Pour commencer, il aborde la définition d'un module dans Drupal 8 et la façon de convertir le fichier info au format YAML.

Dans la communauté française, nombreux sont les développeurs à avoir créé, au moins une fois, un module contribué. Qu'il dispose d'une version stable ou qu'il soit encore en sandbox, il va être temps de le porter sur Drupal 8 si l'on veut que cette nouvelle version majeure ait un taux de pénétration plus important que Drupal 7 à sa sortie.

Cette série, qui débute par l'article que vous êtes en train de lire, suivra étape par étape la conversion d'un module contribué, Multiple E-mail Addresses, de Drupal 7 vers Drupal 8.

Définition d'un module

Jusqu'à Drupal 7, un module était identifié par le cœur à la condition que l'on trouve, au sein du même répertoire, au moins un fichier <modulename>.info et un fichier <modulename>.module. Le fichier info contient les métadonnées du module : son nom, sa description, sa version, ses dépendances, les fichiers à charger automatiquement (autoload), etc. Le fichier module quant à lui contient les fonctions PHP de base permettant au module de fonctionner et est chargé automatiquement par Drupal si le module est actif.

Depuis le 17 mars 2015, la définition d'un module a légèrement changé dans Drupal 8. Désormais, seul un fichier <modulename>.info.yml est obligatoire. Si un fichier <modulename>.module existe, il sera toujours chargé automatiquement, mais il n'est plus obligatoire pour que le cœur de Drupal reconnaisse l'existence du module. Ce changement est dû au fait que la plupart du code qui se trouvait auparavant obligatoirement dans le fichier <modulename>.module est réparti dans d'autres fichiers source, chargés automatiquement grâce à la spécification PSR-4 de PHP, ou dans des fichiers de configuration séparés.

Initialisation du répertoire

La bonne pratique pour mettre à jour votre module d'une version majeure à une autre consiste à partie des sources de la dernier version disponible et de remplacer les fichiers et le code au fur et à mesure que l'on rencontre des erreurs. Ces erreurs peuvent survenir lors de l'exécution du module ou, dans le meilleure des cas, lors de l'exécution des tests. Nous reviendrons, au fil des articles de cette série, sur les erreurs que vous risquez de rencontrer et nosu tenterons de vous aider à comprendre ce qui se cache derrière et comment les résoudre.

De plus, si la branche principale de votre module évolue avant que vous n'ayez terminé votre portage, vous pourrez profiter de la puissance de git pour récupérer les nouvelles fonctionnalités facilement dans votre nouvelle version.

Nous débutons donc par un :

$ git clone --branch 7.x-1.x <pseudo>@git.drupal.org:project/multiple_email.git
$ git branch -b 8.x-1.x

Le fichier info

Comme dit précédemment, tout ce dont nous avons besoin pour pouvoir installer notre module est un fichier <modulename>.info.yml valide. Ce fichier est l'équivalent de l'ancien fichier <modulename>.info mais dans un format différent, le format YAML. Je passerai rapidement sur ce format étant donné qu'il existe de la documentation un peu partout sur Internet et même en français. En quelques mots il s'agit d'un format de sérialisation de données simple, standard et compréhensible par l'humain.

Le cas de Multiple E-mail Addresses étant trop simple pour servir d'exemple, voyons un cas générique qui recense les principales entrées que l'on trouve habituellement dans un fichier info.

Avant : <modulename>.info

name = "Nom du module"
description = "Description du module"
core = 7.x
package = Other
dependencies[] = entity
dependencies[] = entityreference
files[] = modulename.test
files[] = views/modulename_handler.inc
configure = admin/configure/modulename

Après : <modulename>.info.yml

name: 'Nom du module'
description: 'Description du module'
core: 8.x
type: module
package: Other
dependencies:
 - entity
 - entityreference
configure: modulename.admin.settings

Pour entrer dans le détail vous remarquerez tout d'abord que la plupart des entrées sont identiques ; les symboles "=" ont simplement été remplacés par des symboles ":" à la manière du format JSON.

De plus, vous noterez l'apparition d'une nouvelle entrée obligatoire qui n'existait pas dans les versions précédentes et qui permet de définir le type de projet dont il s'agit : module, theme ou profile. Cette entrée a été créée pour permettre le changement autorisant un module ou un profil d'installation à n'être défini que par son fichier <modulename>.info.yml. Auparavant, c'était la présence d'un fichier <modulename>.module ou <modulename>.profile qui permettait à Drupal de faire la différence.

Ensuite, les dépendances suivent le format des collections YAML en passant sous la forme d'une liste préfixée d'un tiret. Elles auraient également pu être décrites sur une ligne entre crochets et séparées par des virgules comme suit : 

dependencies: [entity, entityreference]

Les entrées files ont quant à elles simplement disparu. En effet, comme dit précédemment, Drupal 8 suit la spécification d'auto-chargement PSR-4 et il n'est donc plus nécessaire de déclarer les fichiers qu'il faudra charger puisqu'ils seront inclus automatiquement lors de l'appel à leur contenu.

Enfin, l'entrée configure, qui permet d'afficher un lien vers l'URL de votre choix directement depuis la liste des modules, est réécrite en utilisant le nouveau système de routes dont nous reparlerons dans le troisième volet de cette série d'articles. Cette entrée est optionnelle donc je vous recommande de l'omettre pour commencer et de vous laisser une note pour la rajouter plus tard.

Si vous voulez voir un autre exemple, Nicolas a déjà rédigé un billet sur ce blog qui se base sur le fichier info du thème Seven présent dans le cœur de Drupal.

Le résultat sur multiple_email.info.yml

name: 'Multiple E-mail Addresses'
type: module
description: 'Allows users to have more than one registered e-mail address.'
core: 8.x
# @TODO add configure route

Le fichier install

La plupart des modules existants incluent un fichier <modulename>.install contenant des implémentations de hooks joués lors de l'installation ou de l'activation du module. Drupal 8 a vu de nombreux changements opérés au sein de ce fichier. Certains concepts et les hooks associés ont disparu comme l'activation et son hook_enable ainsi que la désactivation et son hook_disable. D'autres n'ont pas vraiment changé comme les hook_schema, hook_install et hook_uninstall bien que leur contenu puisse être différent.

Étant donné que nous avons copié la totalité des fichiers de la version 7.x du module, il est probable qu'un fichier <modulename>.install existe dans votre répertoire, ce qui risque de poser des problèmes pour activer le module. Par exemple, lorsque l'on tente d'activer le module multiple_email, nous obtenons l'erreur suivante : 

$ drush en -y multiple_email
The following extensions will be enabled: multiple_email
Do you really want to continue? (y/n): y
exception 'PDOException' with message 'SQLSTATE[42S22]: Column not found: 1054 Unknown column 'mail' in    [error]
'field list'' in /[...]/core/lib/Drupal/Core/Database/Statement.php:61
[... Stack trace ...]

Next exception 'Drupal\Core\Database\DatabaseExceptionWrapper' with message 'SQLSTATE[42S22]: Column not
found: 1054 Unknown column 'mail' in 'field list': 
    INSERT INTO {multiple_email}
      (uid, email, time_registered, confirmed)
    SELECT
      uid,
      mail,
      created,
      1
    FROM {users}
    WHERE uid != 0
    AND mail != ''
    GROUP BY mail
  ; Array
(
)
' in /[...]/core/lib/Drupal/Core/Database/Connection.php:609
[... Stack trace ...]

L'erreur étant survenue lors de l'installation du module, il est probable qu'elle provienne du hook_install. Vérification faîte, c'est bien ce hook qui tente d'exécuter la requête SQL incriminée. En y regardant de plus près, l'erreur dit qu'il n'y a pas de colonne mail dans la table cible. Un coup d'œil dans la base de données nous montera qu'en effet, la table users ne contient plus de colonne mail.

En attendant que nous abordions la migration de ces hooks en détail dans un chapitre prochain, je vous recommande de commenter la totalité du code de ce fichier afin d'éviter les erreurs à l'installation et à la désinstallation du module.

Conclusion

Maintenant que vous avez commenté votre fichier <modulename>.install et converti votre fichier <modulename>.info, vous n'avez plus qu'à activer votre module dans Drupal pour vérifier qu'aucun morceau de code resté dans le fichier <modulename>.module n'entre en conflit avec le cœur de Drupal 8.

La prochaine étape, couverte dans le deuxième article de la série, sera de transformer les tests de votre module pour qu'ils puissent être exécutés dans Drupal 8. Cela vous permettra de simplfiier le travail de mise à jour du module tout en évitant les régressions.

Commentaires

Cellou Mercredi 20 mai 2015 - 14:27
Très bel article, clair et précis. Existe t-il une méthode sur drupal.org (ou ailleurs) pour afficher la liste de tous les modules qui ne sont pas encore portés sur Drupal 8? Merci et hâte de voir la suite.

Votre commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement.
Votre adresse servira à afficher un Gravatar et à vous notifier des réponses. Votre commentaire sera anonymisé si ce billet est dépublié pendant plus de 3 mois.

À propos de Edouard

Expert technique

Après un premier contact douloureux avec Drupal en 2009 en autodidacte, j'ai suivi une formation qui m'a convaincu de mon choix technologique et m'a vraiment mis en selle. Durant plusieurs années suite à cela j'ai accompagné des entreprises locales dans le développement de leurs projets de toutes sortes, de la simple vitrine à l'intranet social en passant par le projet e-commerce.