Makina Blog
Migration d'un site Drupal 7 en Drupal 11
Trucs, astuces et "bouts" de code pour migrer votre site web de Drupal 7 à Drupal 11. Compte-rendu d'une conférence donnée au Drupalcamp Rennes 2024.
Cet article a été transformé suite à notre conférence à la Drupalcamp Rennes 2024, afin de le mettre à jour pour Drupal 10. Il reste entièrement valable pour Drupal 11.
Nous avons ces dernières années migré de nombreux sites clients de Drupal 7 à D9, D10 ou Drupal 11. L'objet de cet article est de vous présenter les enseignements que nous en avons tiré.
Une migration, ça se prépare !
Un peu comme un déménagement, une migration se prépare : on commence par nettoyer le site existant, notamment en supprimant les contenus et fonctionnalités inutiles. On en profite également pour mettre à jour le site aux dernières versions Drupal 7 du core et des modules communautaires (pour s'assurer du bon fonctionnement des scripts automatiques de migration).
C'est également le bon moment pour identifier des contenus de tests : ce sont des contenus éditoriaux assez complets qui vous permettront de valider l'intégralité du bon fonctionnement de la migration.
Attention aux features !
La grande majorité des sites web Drupal 7 utilisent le module Features, qui permet d'externaliser la configuration dans du code. Le problème est qu'alors les scripts de migration ne tiendront pas compte de cette configuration. Nous vous recommandons d'utiliser un patch de Features (issue #663136) permettant d'"importer" l'intégralité de la configuration des Features en base de données, permettant une migration complète de cette configuration.
Déroulé de la migration
Il y a essentiellement 2 façons de migrer un site de Drupal 7 à Drupal 8+ :
- 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 11, 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 retenons, pour permettre d'agir sur les nombreux points qui peuvent ne pas se comporter comme nous le souhaitons.
Migration automatisée
Ici, la procédure est simple :
- Installer un Drupal 9 vierge avec le profil d'installation "Standard", la migration va de toute façon écraser toute la configuration, donc inutile de perdre du temps avec une installation plus compliquée. Nous choisissons plutôt le standard que le profil minimal.
- Activer l'intégralité des modules que vous souhaitez migrer, y compris des modules de la communauté, notamment les modules qui fournissent des champs ou de la configuration importante pour la migration.
- Attention, il est recommandé de récupérer également le module "Media Migration", qui réalisera la transformation de tout ce qui concerne les fichiers en Drupal 7 sous forme de Media en Drupal 11. Ce module est pas loin de faire de la magie, donc ne passez pas à côté !
- 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é.
Un outil communautaire
Vous pouvez également utiliser un outil fourni par la communauté, Acquia Migrate: Accelerate, complètement clé en main, qui réalise votre migration entièrement, grâce à une énorme base de connaissance sur les possibilités de migration :
La principale limitation de cet outil est qu'il migre en Drupal 9, en utilisant Drupal 7. Cela demande donc un peu plus de travail d'obtenir une migration Drupal 11.
De notre côté, nous utilisons un outil interne qui effectue un certain nombre de requêtes SQL pour auditer la base de données source, et anticiper une partie des problèmes. C'est notre base de connaissance à nous, bien sûr moins complète que celle d'Acquia, mais qui nous assiste pour les migrations.
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:^4 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 --group=migrate_drupal_7 --tag='Configuration' --execute-dependencies --continue-on-failure
Cette migration va potentiellement générer des erreurs, il faut donc les corriger, puis exécuter à nouveau la migration. À ce stade, on va souvent exporter la configuration, puis ne plus jamais lancer ces migrations de configuration. En effet, il est désormais plus rapide pour votre développement de corriger la configuration directement au sein de Drupal plutôt que de travailler sur les fichiers yml générés…
drush migrate:import --group=migrate_drupal_7 --tag='Content' --execute-dependencies --continue-on-failure
Cette migration va également générer des erreurs à corriger. C'est également à cette étape que nous pouvons améliorer le site, transformer un type de contenu en entité ou en média, en tirant partie des améliorations des versions récentes de Drupal.
C'est cette démarche que nous utilisons systématiquement, notamment pour la partie "amélioration du site", qui permet d'éliminer une bonne partie de la dette technique Drupal 7 et faire comme si c'était un nouveau projet Drupal 11.
The good
Conditionné par mon passé Drupaliste, j'appréhendais un peu la migration des contenus… Et bien c'est ce qui se passe le mieux : la configuration des contenus, au sens Drupal 8, et donc des objets "contenu", "taxonomie", "utilisateurs" se passe plutôt bien, 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 peut être récupérée directement, avec éventuellement quelques ajustements basiques.
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. Un énorme travail a été réalisé dans la communauté Drupal sur ce sujet, et ce n'est désormais plus du tout un souci.
Les modules non compatibles Drupal 11
Il existe encore de nombreux modules communautaires qui ne sont pas compatibles avec Drupal 11. Cependant, la plupart de ces modules disposent d'une version Drupal 8 ou 9 ainsi que d'un patch de compatibilité. Pour les installer, il suffit alors d'utiliser Composer Lenient, fourni par la communauté :
composer require mglaman/composer-drupal-lenient composer config --merge --json extra.drupal-lenient.allowed-list '["drupal/token"]'
The bad
Contenu + Configuration = ?!?!
Nous souhaitons 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'avons plus de raison de relancer puisqu'après les avoir lancés une fois, nous avons 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 devons donc adapter une partie des migrations. Par exemple, la migration des utilisateurs ne pouvant plus se baser sur une migration automatisée des rôles, nous remplaçons simplement 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 !
Certaines configurations ne se migrent vraiment pas correctement.
Les vues (du module Views) 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).
Les webforms ne se migrent également pas toujours très bien.
Cependant, compte-tenu des différences techniques importantes entre Drupal 7 et Drupal 11, il est parfois plus rapide de tout refaire, notamment quand le nombre des vues ou webforms est limité.
Le multilinguisme, c'est pas si simple
La migration des alias multilingues fait parfois à peu près n'importe quoi, et nous avons déjà du là 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
Sur certains sites Drupal 7, 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.
Le code personnalisé
Là aussi, il existe des solutions techniques pour récupérer une partie du code personnalisé :
- Si vous êtes pressés, la promesse de Retrofit est de vous permettre d'utiliser votre code Drupal 7 sous Drupal 11, le temps que vous le convertissiez petit à petit :
composer require retrofit-drupal/retrofit
- Sinon, il existe un module qui vous assistera dans la transformation de votre code, Drupal 7 to 8/9/10/11 module upgrader :
composer require drupal/drupalmoduleupgrader drush dmu-analyze mon_module
Mais de façon plus générale, beaucoup de choses ont changé dans Drupal, et il est courant de notre côté de supprimer tout ou partie du code Drupal 7 pour l'implémenter différemment, ne conservant que ce qui ne peut pas être transformé. Cela réduit de beaucoup la taille du code à réécrire.
Les médias
Comme toujours (en tout cas en Drupal), le point le plus critique des migrations concerne les médias. Même en utilisant "Media Migration", qui reste la meilleure solution, compatible avec l'ensemble des possibilités Drupal 7 de gestion des médias, il reste souvent des points à ajuster, notamment en ce qui concerne les liens entre les médias et l'éditeur de texte riche.
Attention, la migration des médias recherche les fichiers dans sites/default/files, et si vous êtes par exemple sur un multisite, vous devrez retravailler ce point de la migration.
De même, Media Migration utilise par défaut des drupal_entity dans l'éditeur de texte riche (compatibles avec le module entity_embed), et si vous souhaitez utiliser des "drupal_media", il vous faudra ajouter la ligne suivante dans votre settings.php :
$settings['media_migration_embed_token_transform_destination_filter_plugin'] = 'media_embed';
Enfin, le filtre "autop" (qui transforme balises <p> et <br>) doit se trouver en dernier dans votre éditeur de texte riche pour éviter de couper les liens sur les images dans les éditeurs de texte.
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
Nous utilisons sur notre projet quelques patchs pas encore intégrés à leurs modules respectifs.
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
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
Actualités en lien
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.
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.
Utiliser Migrate en Drupal 8
Trucs, astuces et points d'attention pour l'import de données avec Migrate en Drupal 8.