Migrer Ibexa Commerce 4.6 vers Ibexa 5.0 sans casser sa boutique — REX complet
Migrer Ibexa Commerce ne consiste pas à modifier une contrainte Composer et à relancer l'application. Ibexa 5 fait évoluer PHP, Symfony, le frontend et surtout le schéma SQL historique. Ce retour d'expérience décrit une migration réelle d'une boutique de démonstration Commerce 4.6 avec produits, variantes, stock, promotions, clients et commandes.
Résultat du laboratoire : l'application fonctionne sous Ibexa Commerce 5.0.11, Symfony 7.4 et PHP 8.3. Les 10 produits, 5 commandes et 3 remises de départ ont été conservés.
Le saut technique en une vue
| Couche | Avant | Après dans ce REX | Conséquence |
|---|---|---|---|
| Ibexa Commerce | 4.6.32 puis 4.6.33 | 5.0.11 | migrations SQL et contenu obligatoires |
| Symfony | 5.4 LTS | 7.4 LTS | deux versions majeures et suppression d'API dépréciées |
| PHP | contrainte >=7.4 |
contrainte >=8.3 |
typage plus strict et anciennes compatibilités supprimées |
| Doctrine DBAL | génération 3 antérieure | 3.9 via Ibexa 5 | schéma historique renommé et modernisé |
| Routage | annotations | attributs PHP | suppression de annotations.yaml |
| Sécurité | Guard et options historiques | nouveaux authenticators Symfony | réécriture des firewalls Ibexa |
| Swiftmailer | Symfony Mailer | suppression des fichiers Swiftmailer | |
| Frontend | ancien Encore/CKEditor/React | Encore 5, CKEditor 5 récent, React 19 | nouvelle chaîne Node/Yarn/TypeScript |
Ibexa 5.0.0 reposait initialement sur Symfony 7.3. Ce projet cible toutefois Ibexa 5.0.11 : depuis Ibexa 5.0.7, la branche 5.0 exige Symfony 7.4. C'est pourquoi les contraintes finales sont en ^7.4 et non en ^7.3.
Le point le plus important avant de commencer
La documentation Ibexa demande de partir de la dernière maintenance 4.6 et d'exécuter les migrations Doctrine avant de mettre à jour les dépendances vers 5.0.
Dans notre cas, l'instance était encore en 4.6.32. Un premier dry-run lancé directement avec les paquets 5.0 proposait 86 migrations et commençait par recréer tout le schéma :
CREATE TABLE ibexa_setting (...)
CREATE TABLE ezbinaryfile (...)
...
Une exécution aveugle aurait échoué avec une erreur du type :
Table 'ibexa_setting' already exists
La correction a été de préparer temporairement le runtime 4.6.33, d'exécuter ses migrations, puis de reprendre la migration 5.0. Après cette étape, Ibexa 5 voyait correctement :
Executed: 28
New: 58
Ce contrôle est plus fiable que de marquer manuellement des migrations comme exécutées.
1. Sauvegarder avant toute modification
Nous avons conservé :
- un dump SQL complet ;
composer.jsonetcomposer.lock;package.jsonetyarn.lock;symfony.lock;- des sommes SHA-256 pour contrôler les sauvegardes.
Le dump doit être fait avant les migrations 4.6.33 puis conservé jusqu'à la validation fonctionnelle complète.
2. Isoler l'outillage du projet
Pour ne pas casser les autres projets locaux :
- PHP global n'a pas été remplacé ;
- Composer 2.11 a été installé dans un chemin utilisateur séparé et appelé explicitement ;
- Node et Yarn sont épinglés dans
package.jsonavec Volta ; - aucun conteneur PHP d'un autre projet n'a été modifié.
Configuration finale utilisée pour le frontend :
{
"packageManager": "yarn@1.22.22",
"volta": {
"node": "24.11.0",
"yarn": "1.22.22"
}
}
Pourquoi Node 24 alors qu'Ibexa annonce Node 22 ou supérieur ? La recette récupérait @ckeditor/ckeditor5-dev-utils 61.x, dont le moteur déclaré exige Node 24.11. Nous avons préféré respecter cette contrainte plutôt que masquer l'erreur avec --ignore-engines.
3. Vérifier le code avec Rector avant la base
Rector a été limité au code du projet :
return RectorConfig::configure()
->withPaths([__DIR__ . '/src'])
->withSets([IbexaSetList::IBEXA_50->value])
->withPhpSets(php83: true)
->withComposerBased(symfony: true);
Commande de contrôle :
php vendor/bin/rector process --dry-run --no-progress-bar
Résultat : aucun changement applicatif nécessaire dans src/.
4. Mettre à jour les dépendances
Les contraintes principales deviennent :
{
"php": ">=8.3",
"ibexa/commerce": "5.0.11",
"symfony/framework-bundle": "^7.4"
}
Les dépendances directes ibexa/discounts et ibexa/discounts-codes ont été retirées : elles sont désormais apportées par le métapaquet Commerce.
La mise à jour a été faite sans scripts dans un premier temps :
composer update --with-all-dependencies --no-scripts
Cela permet de résoudre le graphe de dépendances avant de lancer un cache clear ou un build avec une configuration encore en cours de migration.
5. Comprendre le passage de Symfony 5.4 à 7.4
Ce n'est pas une simple montée de version mineure. Symfony 7 retire les mécanismes qui étaient seulement dépréciés dans Symfony 6. Le principe suivi a donc été : corriger le code avec Rector, remplacer les anciennes configurations, puis seulement reconstruire le conteneur.
Routage : annotations vers attributs
Avant, le projet chargeait les contrôleurs avec un fichier config/routes/annotations.yaml. Après migration :
controllers:
resource:
path: ../src/Controller/
namespace: App\Controller
type: attribute
Le fichier annotations.yaml a été supprimé. Les contrôleurs personnalisés doivent utiliser les attributs PHP, par exemple #[Route(...)].
Sécurité : abandon de Guard
La configuration 4.6 contenait encore des notions comme anonymous, guard.authenticator et csrf_token_generator. Le firewall 5.0 repose sur le nouveau système d'authentification Symfony :
security:
password_hashers:
Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface: auto
firewalls:
ibexa_front:
provider: ibexa
user_checker: Ibexa\Core\MVC\Symfony\Security\UserChecker
context: ibexa
form_login:
enable_csrf: true
login_path: login
check_path: login_check
custom_authenticators:
- Ibexa\PageBuilder\Security\EditorialMode\FragmentAuthenticator
entry_point: form_login
Le REST possède désormais son propre firewall /api/ibexa et son RestAuthenticator. Il ne faut pas recopier aveuglément l'ancien bloc Guard : le conteneur peut compiler alors que l'authentification éditoriale ou REST reste cassée.
Validation et Messenger
La validation par annotations devient une validation par attributs :
framework:
validation:
enable_attributes: true
L'option Messenger reset_on_message utilisée par l'ancienne configuration a été retirée, car elle n'est plus acceptée par Symfony 7.
Swiftmailer disparaît
Les fichiers suivants ont été supprimés :
config/packages/swiftmailer.yaml
config/packages/dev/swiftmailer.yaml
config/packages/test/swiftmailer.yaml
La variable historique MAILER_URL a également disparu. Les envois doivent désormais passer par Symfony Mailer et sa configuration MAILER_DSN lorsqu'un transport réel est nécessaire.
Le nouveau répertoire partagé de Symfony 7.4
Symfony 7.4 distingue le cache de build et le répertoire partagé de l'application. Sur une infrastructure avec volumes persistants ou système de fichiers en lecture seule, il faut prévoir var/share en plus de var/cache et var/log.
Autre changement important pour Ibexa : un simple cache:clear ne vide plus forcément le cache de persistance. Après une modification de configuration ou de Page Builder, utiliser explicitement le pool :
php bin/console cache:pool:clear cache.tagaware.filesystem
php bin/console cache:clear
Si Redis est activé, le pool peut être cache.redis selon la configuration du projet.
config/reference.php
Symfony 7.4 génère ce fichier de référence dans le cadre de son nouveau format de configuration PHP. La documentation propose d'envisager son versionnement. Dans notre environnement, le fichier généré contenait toutefois un chemin absolu vers le poste de développement, provenant de la configuration d'un bundle tiers. Nous l'avons donc exclu du dépôt. La bonne décision dépend du contenu réellement généré : il faut l'inspecter avant de le committer, pas l'ajouter automatiquement.
6. Appliquer puis relire les recettes
Les recettes Ibexa et Webpack ont été réinstallées avec --reset. Cette opération est utile, mais elle remplace aussi des valeurs personnalisées.
Dans notre boutique, deux réglages métier avaient disparu pendant le reset :
ibexa:
repositories:
default:
product_catalog:
regions:
default:
vat_categories:
standard:
value: 20
et :
ibexa:
system:
storefront_group:
storefront:
catalog: demo_catalog
Sans cette relecture, la configuration serait techniquement valide, mais le storefront ne ciblerait plus notre catalogue de démonstration.
Autres adaptations rencontrées :
- suppression de l'ancien
IbexaIconsBundle; - suppression de Swiftmailer au profit de Symfony Mailer ;
- remplacement des annotations Symfony par des attributs ;
- retrait de l'option Messenger
reset_on_message, supprimée en Symfony 7 ; - remplacement de l'ancien authenticator Page Builder par le
FragmentAuthenticator; - ajout des routes API Platform, Collaboration, Share et Connector AI.
7. Construire le frontend
Le build suit cet ordre :
- installation Yarn ;
- génération du
tsconfigIbexa ; - compilation du bundle applicatif ;
- génération des traductions ;
- compilation des bundles Ibexa, internes, bibliothèques et RichText.
Une première compilation a échoué parce que public/bundles contenait encore les assets 4.6. La solution :
php bin/console assets:install public
Après réinstallation :
- bundle
app: succès ; - BO Ibexa : succès ;
- storefront : succès ;
- React/ReactDOM : succès ;
- RichText/CKEditor : succès.
Les avertissements de dépréciation Sass proviennent des dépendances Ibexa/Bootstrap ; ils n'ont pas empêché la compilation.
8. Initialiser correctement les migrations sous 4.6.33
Le dry-run 4.6.33 a détecté 28 migrations et seulement 6 requêtes réellement utiles. Les installations de schéma ont vu que les tables existaient déjà et n'ont rien recréé.
Les changements utiles comprenaient notamment :
- rendre
ibexa_product_specificationnon traduisible ; - nettoyer d'anciennes limitations de rôle ;
- nettoyer des validateurs Form Builder ;
- ajouter les index Taxonomy manquants.
Après validation du dry-run :
php bin/console ibexa:doctrine:migrations:migrate --no-interaction
9. Prévisualiser puis appliquer les migrations 5.0
Toujours générer le SQL avant l'exécution :
php bin/console ibexa:doctrine:migrations:migrate \
--dry-run \
--write-sql=var/migration-preview-5.sql \
--no-interaction
Le second dry-run contenait 58 migrations et 317 requêtes simulées. L'exécution réelle a appliqué 288 requêtes en moins de dix secondes.
Ibexa 5 renomme une grande partie du schéma historique :
| Ibexa 4.6 | Ibexa 5.0 |
|---|---|
ezcontentobject |
ibexa_content |
ezcontentclass |
ibexa_content_type |
ezcontentobject_attribute |
ibexa_content_field |
ezuser |
ibexa_user |
ibexa_taxonomy_entries |
ibexa_taxonomy_entry |
ibexa_product_type_settings |
ibexa_product_type_setting |
La migration crée aussi les schémas des fonctions maintenant intégrées : Collaboration, Share, attribut Symbol, attribut DateTime et Connector AI.
10. Exécuter les migrations de contenu
Trois migrations YAML officielles restent nécessaires :
- Tags/Taxonomy ;
- catégories du catalogue produit ;
- comptes entreprise.
Exemple :
php bin/console ibexa:migrations:import \
vendor/ibexa/taxonomy/src/bundle/Resources/install/migrations/2025_08_09_14_47_mark_tag_as_container.yaml
php bin/console ibexa:migrations:migrate \
--file=2025_08_09_14_47_mark_tag_as_container.yaml \
--siteaccess=admin \
--no-interaction
Le même principe a été appliqué aux deux autres fichiers fournis par Product Catalog et Corporate Account.
11. Adapter nos migrations de démonstration
Pour pouvoir rejouer la formation sur une installation Ibexa 5 neuve, les identifiants de FieldType ont été normalisés :
| Ancien identifiant | Identifiant Ibexa 5 |
|---|---|
ezstring |
ibexa_string |
ezimage |
ibexa_image |
ezrichtext |
ibexa_richtext |
Le FieldType ibexa_product_specification reste la porte d'entrée qui transforme un Content Type en Product Type. Prix et stock restent gérés dans les tables Commerce dédiées, pas dans la valeur de ce champ.
12. Vider les caches et réindexer
php bin/console cache:pool:clear cache.tagaware.filesystem
php bin/console ibexa:reindex
php bin/console cache:clear
La réindexation a traité les 36 contenus existants avec le moteur Legacy SQL.
13. Contrôles effectués
Contrôles techniques :
- Composer valide ;
- Rector 5 en dry-run : OK ;
- 173 fichiers YAML : OK ;
- 5 templates Twig : OK ;
- conteneur Symfony : OK ;
- migrations Doctrine : à jour ;
- compilation frontend complète : OK.
Contrôles fonctionnels :
- accueil : HTTP 200 ;
- connexion BO : HTTP 200 ;
- catalogue/storefront : route disponible ;
- panier : HTTP 200 ;
- 10 produits conservés ;
- 5 commandes conservées ;
- 3 promotions conservées.
Ce que je retiens
Le risque principal n'était ni PHP 8.3 ni Symfony 7.4. C'était l'ordre des migrations de base. Le passage préalable en 4.6.33 transforme une migration dangereuse en procédure contrôlée et reproductible.
Les recettes sont indispensables, mais elles ne connaissent pas les choix métier du projet. Il faut systématiquement comparer les configurations avant/après, notamment SiteAccess, catalogue, régions, TVA, sécurité et frontend.
Enfin, le dry-run SQL n'est pas décoratif : c'est lui qui nous a montré que l'historique Doctrine n'était pas initialisé et qui a évité une migration cassée.
Checklist rapide
- [x] sauvegarde SQL et fichiers de verrouillage ;
- [x] dernière 4.6 installée temporairement ;
- [x] migrations 4.6 exécutées avant la 5.0 ;
- [x] dépendances Ibexa 5/Symfony 7.4 ;
- [x] recettes relues et personnalisations restaurées ;
- [x] frontend compilé ;
- [x] dry-run SQL relu ;
- [x] migrations SQL et contenu appliquées ;
- [x] cache vidé et index reconstruit ;
- [x] données Commerce et routes principales vérifiées.
