Makina Blog

Le blog Makina-corpus

Migration d'un site Drupal 7 en Drupal 9


Trucs, astuces et bouts de code pour migrer votre site web de Drupal 7 à Drupal 9

Nous avons récemment migré un site client de Drupal 7 à Drupal 9. L'objet de cet article est de vous présenter les enseignements que nous en avons tiré.

Présentation du site à migrer

Le site Drupal 7 comprenait :

  • Du contenu multilingue (implémenté en Entity Translation, en anticipation du système similaire utilisé dans Drupal 8, avec naturellement la suite de module Internationalization) ;
  • Des médias (implémentés par le module Media, version 2.x) ;
  • Une mise en page "classique" (réalisée par des blocs Drupal) ;
  • Une couche de thème réalisée en Twig, en tentant là aussi d'anticiper ce que serait le cœur Drupal 8.

Comme vous le constatez, nous avons tenté d'anticiper une partie des développements en cours sur Drupal 8 lors de la réalisation du site en Drupal 7. Mais ces anticipations n'ont pas toujours donné les résultats escomptés.

Déroulé de la migration

Il y a essentiellement 2 façons de migrer un site de Drupal 7 à Drupal 9 :

  • Mettre le site à jour le site en utilisant les scripts de migration fournis par le cœur de Drupal (basé, depuis Drupal 8, sur le module Migrate issu de la communauté et désormais dans le cœur) ;
  • Créer entièrement un nouveau site Drupal 9, et tenter de récupérer les contenus de Drupal 7 (là encore en utilisant des outils basés sur Migrate).

En général, nous recommandons la deuxième solution, c'est celle qui souvent génère le moins de contraintes en promettant une transformation des contenus selon les besoins.

Dans le cas de ce projet, nous étions confiants sur l'implémentation "standard" (proche du cœur) du site, avec très peu de personnalisation, et nous avons, après une phase de prototypage, décidé de tester la première solution, basée sur une migration automatisée.

Une fois cette solution choisie, il reste deux possibilités techniques pour exécuter la migration :

  • Soit de façon totalement automatisée, générée puis lancée par Drupal ;
  • Soit de façon semi-automatisée, en utilisant Drupal pour générer la migration, puis en permettant une phase de modification manuelle de la migration, avant de la lancer finalement.

C'est naturellement cette deuxième option que nous avons retenue, pour permettre d'agir sur les (nombreux) points qui ne se comportaient pas comme nous le souhaitions.

Migration automatisée

Ici, la procédure est simple :

  • Installer un Drupal 9 vierge avec le profil d'installation "Minimal" (la migration va de toute façon écraser toute la configuration, donc inutile de perdre du temps avec une installation plus compliquée) ;
  • Activer l'intégralité des modules que vous souhaitez migrer (y compris des modules de la communauté) ;
  • Activer le module "Migrate Drupal UI", et si vous êtes sur un site multilingue, le module "Migrate Drupal Multilingual" (aujourd'hui disparu, puis fini, donc intégré dans le core) ;
  • Aller dans l'interface de migration ("Configuration" / "Development" / "Upgrade") et lancer la migration en indiquant les paramètres de la base de données source.
 
Vous obtenez alors un site avec le contenu migré.

Migration semi-automatisée

Cette deuxième façon de réaliser une migration partiellement automatisée s'appuie sur la précédente : au lieu de faire la migration, on génère le code associé à la migration (des fichiers .yml pouvant être importés via la gestion de configuration de Drupal), ce qui nous permet d'agir dessus (et d'éventuellement corriger des points) avant de la lancer.

composer require drupal/migrate_upgrade:^3@dev
drush en -y migrate_upgrade
drush migrate-upgrade --legacy-db-url=pgsql://user:password@host:port/postgres --legacy-root=https://domain/ --configure-only

L'option --configure-only est nécessaire pour uniquement générer les migrations au lieu de les lancer.

Une fois les migrations modifiées, on peut les importer par :

drush migrate:import –all

C'est donc cette solution que nous avons choisie d'utiliser, prévoyant suite à un audit préalable que certains ajustements seraient nécessaires.

The good

Conditionné par mon passé Drupaliste, j'appréhendais pas mal la migration des contenus… Et bien c'est ce qui s'est le mieux passé : la configuration des contenus (au sens Drupal 8), et donc des objets "contenu", "taxonomie", "utilisateurs" s'est plutôt très bien passée, en migrant à la fois les contenus, et les liens entre contenus (issus du module Entity Reference).

De ce point de vue là, si vous utilisez la migration automatique, vous ne serez pas déçus, la majorité du contenu a pu être récupérée directement, avec éventuellement quelques ajustements basiques (par exemple, la configuration du module Geofield n'était pas directement fonctionnelle, mais il s'avère que la modification mineure que nous avons opérée de notre côté a depuis été corrigée dans le module).

De même, certaines redirections issues de Drupal 7 ne comportaient pas de code (car il était par défaut en Drupal 7), et faisaient planter la migration. En l'occurence, ajouter une valeur par défaut sur "301" nous a permis de facilement corriger la migration, et c'est en cours de correction sur le module.

Le multilinguisme, en fait, c'est simple !

Mieux que ça, l'intégralité des traductions des contenus et taxonomies a également été correctement récupéré. C'était la vraie bonne surprise de cette migration.

The bad

Contenu + Configuration = ?!?!

Nous souhaitions utiliser le nouveau système de configuration issu de Drupal 8 (familièrement appelé CMI, pour "Configuration Management Initative").

Or, le lancement de la migration de façon entièrement automatisée génère pour l'ensemble des migrations de contenus des dépendances sur des migrations de configuration, que nous n'avions plus de raison de relancer puisqu'après les avoir lancés une fois, nous avions exporté la configuration afin de pouvoir la gérer en utilisant le workflow de développement de site basé sur les commandes "drush configuration:export" (cex) et "drush configuration:import" (cim).

Nous avons donc du adapter une partie des migrations pour cela. Par exemple, la migration des utilisateurs ne pouvant plus se baser sur une migration automatisée des rôles, nous avons simplement remplacé la dépendance (et la recherche basée sur une migration) par une correspondance statique des rôles (upgrade_d7_user.yml) :

 roles:
# -
#  plugin: migration_lookup
#  migration: upgrade_d7_user_role
#  source: roles
-
plugin: static_map
 source: roles
 map:
 2: authenticated
  3: administrator
  4: webmaster

Toute la configuration… ou pas !

De plus, toute la configuration n'est pas forcément récupérée. D'abord, parce que la migration ne peut convertir automatiquement que ce qu'elle connaît, c'est-à-dire ce qui est contenu dans la base de données.

Si votre site Drupal 7 est géré en utilisant le module Features (ce qui est souvent le cas), et que vos Features ne sont pas surchargées (ce qui n'est pas toujours le cas ;-)), alors, pour la migration, cette configuration n'existe pas.

Notamment, nos vues (du module Views), mais également nos formats d'images ou nos configurations d'éditeurs n'étaient pas migrées. Il existe côté Features une possibilité d'importer intégralement les configurations en base (via un patch), mais nous avons préféré refaire ces configurations sur le nouveau site Drupal 9, en raison d'un nombre limité, et pour bénéficier d'éventuelles améliorations Drupal 9 sur ces points.

De toute façon, les vues ne sont pas migrées par la procédure automatique de Drupal. C'est une limitation documentée, et si le module "View Migration" est suggéré par cette page, en pratique nos tests n'ont pas été concluants : l'ensemble des affichages n'était pas exporté, et les exports générés étaient mal formattés et inexploitables.

Les blocs fonctionnant tout à fait différement entre Drupal 7 et Drupal 9 ne sont pas non plus correctement récupérés / traduits, et nous avons donc du les recréer (heureusement, leur nombre était limité dans le site source, ce qui ne nous a pas coûté trop de temps).

Le multilinguisme, c'est pas si simple

Enfin, dans notre cas, la migration des alias multilingues faisait à peu près n'importe quoi, et nous avons du là encore adapter la migration (upgrade_d7_url_alias.yml) :

langcode:
# plugin: null_coalesce
# source:
# - '@node_translation/1'
#  - language
 plugin: get
 source: language

The ugly

Jusqu'à maintenant, avec quelques corrections faciles, on a pu améliorer rapidement l'état de la migration. On passe maintenant à ce qui nous a couté pas mal de temps sur ce projet.

Le thème

Nous étions relativement fiers d'être partis sur Twig pour le site Drupal 7 et… et bien ça n'a pas servi à grand chose. En plus du renommage des templates (de .tpl.twig à .html.twig), opération mineure, il s'avère que de très nombreuses choses ont changées au niveau du thème :

  • D'abord, tout est block, en Drupal 8 : le titre de la page, le fil d'ariane, … et les anciennes variables utilisées dans les templates Drupal 7 n'ont donc plus cours ;
  • Ensuite, de très nombreuses fonctions d'API utilisées dans les templates ou les fonctions de _preprocess (t(), drupal_add_css(), drupal_is_logged_in(), drupal_get_path(), …) ont changées, et une bonne partie du code est donc à convertir ;
  • La gestion d'URLs, basée désormais sur les routes, oblige à revoir également la plupart des chemins utilisés directement dans les templates.
Par contre, nous avons pu ré-utiliser presque directement la feuille de style et le code javascript, une fois transformés en library, la nouvelle façon d'utiliser des JS & CSS depuis Drupal 8 (via le fichier theme.libraries.yml).

Les menus

Vous n'aurez peut-être plus le problème, parce que ça a été corrigé depuis, mais la migration des menus dans un contexte multilingue ne fonctionnait pas du tout, nous avons été obligés de re-créer intégralement le menu.

Les médias

Comme toujours (en tout cas en Drupal), le point le plus critique des migrations concerne les médias. Nous utilisions le module Media couplé au module "Media cKeditor" en Drupal 7.

Le module le plus utilisé étant "Media WYSIWYG", les procédures de migration fournies par certains modules communautaires ne traitent que de ces modules (et donc les plugins de migration ne sont pas compatibles avec "Media cKeditor", ce qui nous a obligé à développer nous-mêmes cette partie.

Les différentes solutions existantes ("Migrate File Entities to Media Entities", "Media Migration", "Migrate Media Handler", …) couvrent toutes une partie du problème, ou une certaine configuration de modules, … et ne répondaient pas à notre besoin, je vous encourage tout de même à les consulter au cas où cela fonctionnerait pour vous.

Snippets / Tips & Tricks

Ce paragraphe contient quelques bouts de codes ou astuces qui vous seront peut-être utiles.

Notre script complet de migration

// Disable auto-redirection because we will create lots of aliases
// and pathauto is enabled.
drush cset redirect.settings auto_redirect 0
// Migrate specific migrations
drush migrate:import --tag=File --continue-on-failure --execute-dependencies
drush migrate:import --tag=Content --continue-on-failure --execute-dependencies
// Remove auto-generated aliases before alias migration
drush pathauto:aliases-delete canonical_entities:node
// Migrate aliases
drush migrate:import --tag=Alias --continue-on-failure --execute-dependencies
// Re-enable auto-redirection from redirect module
drush cset redirect.settings auto_redirect 1
// Re-build caches
drush cr
// Generate new aliases with our updated pathauto rules
// (Redirects from old aliases will be automatically created)
drush pathauto:aliases-generate all canonical_entities:node
// Re-build caches
drush cr
// Disable migrate-related modules
drush pm-uninstall migrate migrate_drupal migrate_plus migrate_tools migrate_upgrade
// Re-build caches
drush cr

Le multiliguisme, c'est compliqué

D'abord, installer un site Drupal en synchronisant la configuration (merci CMI), dans un contexte multilingue, ça continue de poser des problèmes : les traductions ne seront pas forcément correctement récupérées à l'installation, et vous aurez parfois une grosse différence entre la configuration active et la configuration exportée.

Pour résoudre ce problème, nous avons simplement pris l'habitude de rajouter à la fin de nos scripts d'installation une tâche qui devrait être inutile mais qui ne l'est pas : une nouvelle synchronisation de configuration, avec un "drush configuration:import" additionnel.

De même, s'il est beaucoup plus facile aujourd'hui d'associer des traductions à des modules, via 2 lignes ajoutées dans un .info.yml de module :

'interface translation project': [module_name]
'interface translation server pattern': profiles/[profile_name]/modules/custom/[module_name]/%language.po

Ces traductions ne surchargent pas toujours les traductions du cœur de Drupal.

Nous avonc donc du ajouter à notre script de déploiement une commande drush pour recharger les traductions de façon forcée en surchargeant les autres :

drush locale:import fr profiles/[profile_name]/modules/custom/[module_name]/fr.po --type=customized --override=all

Le cas de PostgresSQL

Pour différentes raisons, chez Makina Corpus, nous aimons beaucoup PostgreSQL. Ce qui n'est pas forcément le cas de la communauté Drupal, et en choisissant cette base, il faut prendre conscience que vous vous exposez potentiellement à quelques problèmes spécifiques, par exemple au niveau de la casse.

Cependant, la communauté est de plus en plus concernée par ce problème, et a même des tickets [meta] permettant de lister l'ensemble des problèmes entre Drupal et PostgresSQL, facilitant ainsi le débogage et l'éventuelle correction.

Nos contributions à Drupal

Comme souvent sur un gros projet, nous avons eu l'occasion de faire quelques contributions à Drupal.

Nous avons presque réussi à créer un patch sur le core, mais une autre issue était déjà en cours. Cela dit, nous avons tout de même été crédités : Race condition in ImageStyle::createDerivative().

L'utilisation du multilinguisme provoque toujours quelques questions, notamment sur l'affichage des menus par langage, et nous avons été obligé de patcher le module "sitemap" pour afficher un plan du site par language : Filter Menu Tree By Language (un patch du cœur est nécessaire pour que celui-ci fonctionne également).

Enfin, nous avons réussi (d'après nous) à corriger à vieux problème de "Pathauto" concernant les mises à jour des alias en cas de modification de menu : Pathauto token for node menu hierarchy not working after updating parent node (pour "Pathauto") nécessitant également un patch du core (que nous avons réalisé) : Possible need to change the way MenuLinkManager update menu-item and menu-tree.

Conclusion

Se lancer dans une migration à une nouvelle version de Drupal dans les premiers mois d'une nouvelle version est toujours un exercice périlleux parce que les développements ont lieu dans la communauté en parallèle du projet.

D'ailleurs, si vous vous lancez maintenant dans une migration, il est probable que vous rencontriez moins de problèmes que nous, dans la mesure où de nombreuses choses ont avancé :

  • La migration multilingue dans le cœur est considérée comme terminée (et la migration des menus a en effet été ajoutée depuis que nous avons réalisé le projet) ;
  • Les quelques bugs levés sur les modules multilingues ont été corrigés et publiés ou sont en passe de l'être.

La flexibilité de l'éco-système Migrate reste un de nos outils favoris pour migrer des sites, et nous vous invitons à nous contacter pour vos projets de migration de sites Drupal !

Formations associées

Formations Drupal

Formation Drupal Administrateur

Toulouse Du 22 au 24 mai 2024

Voir la formation

Formations Drupal

Formation Drupal Développeur

À distance (FOAD) Du 2 au 4 avril 2024

Voir la formation

Actualités en lien

Image
Drupal 7 - 8 et 9
27/05/2020

Drupal 9 : préparez-vous !

Dans quelques jours, le 3 juin 2020, aura lieu la sortie de Drupal 9 en version stable. À quels changements s’attendre ? Quel sera l’impact sur les sites développés actuellement en Drupal 8 et Drupal 7 ? Voici quelques informations qui vous permettront de mieux appréhender cet événement et d'en mesurer les impacts.

Voir l'article
Image
Bruxelles_mobilité_drupal
18/09/2017

Retour d'expérience sur la réalisation d'un portail Drupal mêlant cartographie et Open Data

Utilisation de Drupal comme outil centralisateur de flux.

Voir l'article
Image
Drupal 8 logo
01/12/2016

Utiliser Migrate en Drupal 8

Trucs, astuces et points d'attention pour l'import de données avec Migrate en Drupal 8.

Voir l'article

Inscription à la newsletter

Nous vous avons convaincus