Aller au contenu

Migration de Magento vers WooCommerce sur cPanel

Ce guide montre comment planifier et mener une migration de Magento vers WooCommerce sur un serveur cPanel, pour les boutiques Magento 2 comme pour les anciens sites Magento 1. Vous déplacerez les produits, les variations, les clients, les images et l'historique des commandes, vous transformerez les anciennes adresses Magento en redirections 301, et vous saurez quand rester sur Magento est le meilleur choix.

Ce guide s’adresse aux propriétaires et aux administrateurs qui veulent déplacer une boutique Magento vers WooCommerce sur un serveur cPanel, sans perdre de produits, de clients ni de trafic. Il couvre Magento 2, vendu sous le nom Adobe Commerce ou gratuit sous le nom Magento Open Source, et Magento 1, en fin de vie depuis juin 2020. Le déclencheur habituel est le coût. Magento exige des développeurs spécialisés, un hébergement lourd et des mises à niveau régulières que bien des boutiques moyennes ne peuvent pas justifier. La voie pratique consiste à restaurer une copie de la base Magento dans le compte cPanel, à l’importer dans WooCommerce, puis à bâtir les redirections à partir de la table d’URL de Magento. À la fin, WooCommerce sert la boutique, chaque ancienne adresse de produit et de catégorie répond par une redirection 301, et vous avez un plan pour les connexions des clients.

Ce que vous allez accomplir

  • Décider si WooCommerce convient à votre boutique, ou si Magento ou Mage-OS reste le meilleur choix.
  • Comprendre comment Magento stocke un produit et où chaque morceau arrive dans WooCommerce.
  • Restaurer une copie de la base Magento dans cPanel, lisible par un utilisateur limité à SELECT.
  • Importer les produits, les variations, les images, les clients et les anciennes commandes, en vérifiant chaque décompte.
  • Transformer la table url_rewrite de Magento en redirections 301, basculer le DNS et garder un retour arrière prêt.

Prérequis

  • Un accès shell au serveur Magento, ou un vidage de la base et une copie de pub/media.
  • Un compte cPanel avec accès SSH sur le serveur de destination, et WP-CLI, l’outil en ligne de commande de WordPress. Vérifiez avec wp --info.
  • Le client mysql et la commande uapi, présents sur tout serveur cPanel.
  • PHP 8.2 ou 8.3 pour le site WordPress, réglé par domaine dans le MultiPHP Manager de cPanel.
  • L’audit préalable d’une migration WooCommerce, fait en premier, qui cadre les passerelles de paiement, la livraison, les taxes et les courriels.
  • Pour les boutiques Magento 1, la logique est la même, mais quelques noms de tables diffèrent. Chaque étape les signale.

Comment ça fonctionne

Magento stocke les produits selon un modèle EAV, pour entité-attribut-valeur. Chaque produit est une ligne de catalog_product_entity qui ne contient que son identifiant (entity_id), son UGS (unité de gestion des stocks, ou SKU) et son type. Tout le reste, comme le nom ou le prix, se trouve dans des tables de valeurs groupées par type de données, par exemple catalog_product_entity_varchar et catalog_product_entity_decimal. Chaque ligne de valeur porte aussi un store_id, si bien qu’un produit peut avoir un nom différent dans chaque vue de magasin. Une vue de magasin est le terme de Magento pour une langue ou une vitrine à l’intérieur d’un site.

WooCommerce est plus simple. Un produit est une publication dans wp_posts, et son UGS, son prix et son stock sont des lignes de wp_postmeta. Le schéma montre où arrive chaque notion de Magento.

Schéma qui associe les produits Magento, les enfants des produits configurables, les attributs, les catégories, les vues de magasin, les images, les clients et les commandes à leurs équivalents WooCommerce

Trois lignes demandent de l’attention. Un produit configurable est le produit parent de Magento, comme un chandail à capuchon, et chaque taille et couleur est un produit simple distinct qui lui est lié dans catalog_product_super_link. Dans WooCommerce, il devient un produit variable avec une variation par enfant. Les options qui servent à choisir un enfant, comme color et size, deviennent des attributs globaux, stockés comme des taxonomies nommées sur le modèle de pa_color.

Les mots de passe ne suivent pas. Magento 2 stocke le mot de passe de chaque client sous forme de hachage salé, une empreinte à sens unique du mot de passe, calculée avec SHA-256 ou Argon2id. WordPress ne peut vérifier ni l’un ni l’autre, alors prévoyez une réinitialisation pour chaque client. Les commandes arrivent comme historique, pas comme paiements actifs. Votre nouvelle passerelle n’a aucune trace des paiements encaissés par Magento, donc les remboursements des anciennes commandes se font dans le tableau de bord de l’ancienne passerelle. Avant de commencer, lisez la dernière section de ce guide pour confirmer que WooCommerce est la bonne destination.

Étapes

  1. Lisez les paramètres de la base et comptez ce que vous avez. Sur le serveur Magento, depuis le dossier racine de Magento, affichez les paramètres de connexion de app/etc/env.php :

    php -r '$c = include "app/etc/env.php"; print_r($c["db"]["connection"]["default"]); echo $c["db"]["table_prefix"], "\n";'

    Résultat attendu : un tableau qui montre [host], [dbname], [username] et [password], puis le préfixe des tables, habituellement une ligne vide. Magento 1 garde ces valeurs dans app/etc/local.xml. Faites ensuite l’inventaire :

    mysql -u magento -p magento -e "SELECT type_id, COUNT(DISTINCT entity_id) AS products FROM catalog_product_entity GROUP BY type_id; SELECT store_id, code, website_id FROM store; SELECT COUNT(*) AS customers FROM customer_entity; SELECT COUNT(*) AS orders FROM sales_order;"

    Résultat attendu, pour une boutique de taille moyenne :

    +--------------+----------+
    | type_id      | products |
    +--------------+----------+
    | configurable |      184 |
    | simple       |     2317 |
    +--------------+----------+
    +----------+---------+------------+
    | store_id | code    | website_id |
    +----------+---------+------------+
    |        0 | admin   |          0 |
    |        1 | default |          1 |
    +----------+---------+------------+

    Les décomptes de clients et de commandes suivent. Le DISTINCT compte sur Adobe Commerce, où les mises à jour planifiées peuvent créer plusieurs lignes par produit. Le magasin 0 contient les valeurs par défaut, donc cette boutique a une seule vraie vue de magasin. Sur Magento 1, la table des commandes s’appelle sales_flat_order. Notez chaque nombre pour les vérifications finales.

  2. Copiez la base dans le compte cPanel. Sur le serveur Magento, prenez un vidage cohérent sans verrouiller la boutique en ligne :

    mysqldump --single-transaction --no-tablespaces -u magento -p magento | gzip > ~/magento-copy.sql.gz

    Copiez le fichier dans votre dossier personnel cPanel avec scp, puis créez une base et restaurez-la. Les noms de bases cPanel commencent par le nom du compte et un trait de soulignement, ici exmpl_ :

    uapi Mysql create_database name=exmpl_mage
    gunzip < ~/magento-copy.sql.gz | mysql -u exmpl -p exmpl_mage

    Résultat attendu : status: 1 de la part de uapi, puis une demande de mot de passe et aucune autre sortie. Le vidage contient les adresses des clients et les hachages de mots de passe, alors gardez-le hors de public_html et supprimez-le à la fin du projet.

  3. Créez un utilisateur de base en lecture seule. L’importateur n’a besoin que de lire les données Magento, et un utilisateur limité à SELECT ne peut rien modifier par erreur :

    uapi Mysql create_user name=exmpl_mageread password='REPLACE_ME'
    uapi Mysql set_privileges_on_database user=exmpl_mageread database=exmpl_mage privileges=SELECT
    mysql -u exmpl_mageread -p exmpl_mage -e "DELETE FROM sales_order LIMIT 1;"

    Résultat attendu : status: 1 deux fois, puis ERROR 1142 (42000): DELETE command denied to user. Cette erreur prouve que la limite fonctionne. Si vous lisez un jour une base Magento en production plutôt qu’une copie, cet utilisateur devient indispensable.

  4. Vérifiez le format des hachages de mots de passe. Magento 2 écrit chaque hachage sous la forme hash:salt:version. La dernière partie indique l’algorithme :

    mysql -u exmpl_mageread -p exmpl_mage -e "SELECT SUBSTRING_INDEX(password_hash, ':', -1) AS version, COUNT(*) AS customers FROM customer_entity GROUP BY version;"

    Résultat attendu : des lignes comme 1 pour SHA-256, et 2 ou une valeur qui commence par 3_ pour Argon2id. WordPress ne peut vérifier aucun de ces formats, pas plus que les hachages MD5 ou SHA-256 salés de Magento 1. L’étape 8 couvre les réinitialisations.

  5. Installez WordPress et WooCommerce sur un sous-domaine de préproduction. Dans cPanel, créez new.example.com avec la racine ~/new.example.com, pointez son DNS vers ce serveur, et créez une base et un utilisateur vides pour WordPress. Installez ensuite :

    cd ~/new.example.com
    wp core download
    wp config create --dbname=exmpl_wp --dbuser=exmpl_wp --dbpass='REPLACE_ME'
    wp core install --url=https://new.example.com --title='Example Store' \
      --admin_user=admin --admin_email=you@example.com --skip-email
    wp plugin install woocommerce --activate
    wp rewrite structure '/%postname%/' --hard

    Résultat attendu : Success: WordPress installed successfully., puis Success: Installed 1 of 1 plugins. et Success: Rewrite structure set. Fixez dès maintenant les bases des produits et des catégories dans Réglages > Permaliens, car la carte de redirections en dépend. Ce guide garde les valeurs par défaut, /product/ et /product-category/.

  6. Créez les attributs globaux. Listez les attributs qu’utilisent vos produits configurables :

    mysql -u exmpl_mageread -p exmpl_mage -e "SELECT DISTINCT a.attribute_code, a.frontend_label FROM catalog_product_super_attribute s JOIN eav_attribute a ON a.attribute_id = s.attribute_id;"

    Résultat attendu : une ligne par attribut, comme color | Color et size | Size. Créez un attribut global correspondant pour chacun :

    wp wc product_attribute create --name=Color --slug=color --user=1
    wp wc product_attribute create --name=Size --slug=size --user=1

    Résultat attendu : Success: Created product_attribute 1., puis la même ligne avec 2. Notre guide sur les produits variables et les attributs explique comment WooCommerce s’en sert.

  7. Exportez et importez le catalogue. Deux voies s’offrent à vous.

    La première est l’extension FG Magento to WooCommerce de Frédéric Gilles, qui lit directement la base Magento. Installez-la avec wp plugin install fg-magento-to-woocommerce --activate, ouvrez Outils > Importer > Magento, puis entrez localhost, l’utilisateur exmpl_mageread et le préfixe des tables s’il y en a un. La version gratuite transfère les catégories, les produits simples, les images, les stocks et les pages CMS. La page de l’extension range les variations, les clients, les commandes et les redirections d’URL dans l’édition Premium payante, alors vérifiez-y la répartition actuelle.

    La deuxième voie est l’export de Magento. Dans l’administration, ouvrez Système > Transfert de données > Exporter, choisissez Produits et CSV, puis sélectionnez Continuer. Magento 2.4 met la tâche en file d’attente. Si aucun fichier n’apparaît après quelques minutes, démarrez le consommateur de la file depuis la racine de Magento :

    bin/magento queue:consumers:start exportProcessor --max-messages=1
    ls var/export/

    Résultat attendu : un fichier comme catalog_product_20260915_140512.csv. Sur Magento 1, passez par Système > Import/Export > Export. Convertissez les colonnes avec un tableur ou un court script :

    Colonne MagentoColonne WooCommerce
    skuSKU
    product_type (configurable, simple)Type (variable, simple, variation)
    name, description, short_descriptionName, Description, Short description
    price, special_priceRegular price, Sale price
    qty, is_in_stockStock, In stock?
    categories (Default Category/Men/Tops)Categories (Men > Tops)
    url_keyslug, réglé après l’import
    base_image, additional_imagesImages, en URL complètes

    Gardez seulement les lignes où store_view_code est vide, car les autres sont des valeurs propres à une vue de magasin et créent des doublons. La colonne configurable_variations décrit chaque enfant sous la forme sku=MH01-S-Black,size=S,color=Black, avec une barre verticale entre les enfants. Servez-vous-en pour régler le Parent de chaque variation sur l’UGS du produit configurable. Importez d’abord les parents, puis les variations, avec Attribute 1 global réglé à 1. Notre guide de migration de Shopify vers WooCommerce détaille cet import en deux passes.

    Pour les images, copiez le dossier des médias sans le cache d’images redimensionnées de Magento :

    rsync -a --exclude=cache magento-host:/var/www/magento/pub/media/catalog/product/ ~/new.example.com/wp-content/uploads/magento/
    find ~/new.example.com/wp-content/uploads/magento -type f | wc -l

    Résultat attendu : un nombre proche de votre nombre d’images de produits. Dans la colonne Images, /m/h/mh01-black_main_1.jpg devient https://new.example.com/wp-content/uploads/magento/m/h/mh01-black_main_1.jpg, et l’importateur ajoute chaque image à la médiathèque.

  8. Importez les clients et planifiez les réinitialisations. Exportez le fichier principal des clients depuis le même écran d’export, puis ramenez-le aux colonnes user_login, user_email, first_name, last_name et role, avec customer comme rôle :

    wp user import-csv magento-customers.csv --skip-update
    wp user list --role=customer --format=count

    Résultat attendu : une ligne par utilisateur créé, puis un nombre égal à celui de l’étape 1. L’édition FG Premium affirme accepter les mots de passe Magento à la connexion, alors testez un vrai compte si vous l’utilisez. Sinon, envoyez les réinitialisations après la bascule par lots de 200, car bien des serveurs cPanel plafonnent le nombre de courriels qu’un domaine peut envoyer par heure. La première ligne découpe les identifiants des clients en fichiers de 200, et la deuxième envoie le premier fichier :

    wp user list --role=customer --field=ID | split -l 200 - ~/reset-batch-
    xargs wp user reset-password < ~/reset-batch-aa

    Résultat attendu : une ligne Reset password for par utilisateur du premier lot, puis une ligne Success: avec le total. Une heure plus tard, relancez la deuxième ligne avec reset-batch-ab, puis reset-batch-ac, et ainsi de suite jusqu’au dernier fichier listé par ls ~/reset-batch-*. Prévenez vos clients la veille pour que le courriel ne passe pas pour de l’hameçonnage.

  9. Transférez l’historique des commandes comme archive. D’abord, désactivez Commande terminée dans WooCommerce > Réglages > E-mails, sinon chaque ancien client reçoit un courriel. Les commandes terminées réduisent aussi le stock, alors importez-les avant la synchronisation finale des stocks. Extrayez les commandes avec l’utilisateur en lecture seule :

    mysql -u exmpl_mageread -p exmpl_mage -N -e "SELECT increment_id, created_at, customer_email, status, grand_total, order_currency_code FROM sales_order ORDER BY entity_id;" > ~/magento-orders.tsv
    wc -l ~/magento-orders.tsv

    Résultat attendu : un nombre de lignes égal à votre total de commandes. FG Premium peut les importer, ou un script peut créer chaque commande à partir de ces lignes et de sales_order_item, en gardant le numéro de commande Magento dans une méta :

    wp wc shop_order create --user=1 --status=completed --customer_id=42 \
      --billing='{"email":"jane@example.com"}' \
      --line_items='[{"product_id":318,"quantity":1,"subtotal":"49.00","total":"49.00"}]' \
      --meta_data='[{"key":"_magento_increment_id","value":"000000145"}]' --porcelain

    Résultat attendu : le seul identifiant de la nouvelle commande, par exemple 2051. Comptez ensuite les commandes terminées et comparez :

    wp eval 'echo count( wc_get_orders( array( "status" => "completed", "limit" => -1, "return" => "ids" ) ) ), "\n";'

    Résultat attendu : un seul nombre, égal au nombre de lignes de ~/magento-orders.tsv.

  10. Bâtissez la carte de redirections. La table url_rewrite de Magento liste chaque adresse publique dans request_path. Les lignes dont redirect_type vaut 0 sont les URL actives, et entity_type indique vers quoi chacune pointe.

    Flux qui part des lignes url_rewrite de Magento, associées par UGS ou par slug de catégorie, vers les permaliens WooCommerce et les lignes Redirect 301 du fichier .htaccess

    Exportez les chemins des produits avec leur UGS, puis la liste UGS-slug de WooCommerce. Remplacez 1 par le store_id de votre vue de magasin, et wp_ par le préfixe de vos tables WordPress s’il est différent :

    mysql -u exmpl_mageread -p exmpl_mage -N -e "SELECT DISTINCT u.request_path, p.sku FROM url_rewrite u JOIN catalog_product_entity p ON p.entity_id = u.entity_id WHERE u.entity_type = 'product' AND u.redirect_type = 0 AND u.store_id = 1;" > ~/product-paths.tsv
    wp db query "SELECT pm.meta_value, p.post_name FROM wp_postmeta pm JOIN wp_posts p ON p.ID = pm.post_id WHERE pm.meta_key = '_sku' AND p.post_type = 'product';" --skip-column-names > ~/woo-skus.tsv
    awk -F'\t' 'NR==FNR {slug[$1]=$2; next} ($2 in slug) {printf "Redirect 301 /%s https://example.com/product/%s/\n", $1, slug[$2]}' ~/woo-skus.tsv ~/product-paths.tsv > ~/magento-redirects.conf
    wc -l ~/product-paths.tsv ~/magento-redirects.conf

    Résultat attendu : deux nombres de lignes identiques. Un produit a souvent plusieurs chemins, comme mh01-hoodie.html et men/tops/mh01-hoodie.html, et chacun reçoit sa ligne. Un écart correspond à une UGS qui n’a pas été importée. Les slugs des catégories reprennent en général la url_key de Magento, donc ces chemins se transposent directement :

    mysql -u exmpl_mageread -p exmpl_mage -N -e "SELECT request_path FROM url_rewrite WHERE entity_type = 'category' AND redirect_type = 0 AND store_id = 1;" | awk '{p=$1; sub(/\.html$/, "", p); printf "Redirect 301 /%s https://example.com/product-category/%s/\n", $1, p}' >> ~/magento-redirects.conf
    head -3 ~/magento-redirects.conf

    Résultat attendu :

    Redirect 301 /mh01-hoodie.html https://example.com/product/mh01-hoodie/
    Redirect 301 /men/tops/mh01-hoodie.html https://example.com/product/mh01-hoodie/
    Redirect 301 /radiant-tee.html https://example.com/product/radiant-tee/

    Collez le fichier dans ~/new.example.com/.htaccess au-dessus de # BEGIN WordPress, pour garder le bloc hors de la section que WordPress réécrit quand on enregistre les permaliens. Si la boutique utilisait un autre suffixe que .html, ajustez le motif de sub(). Sur Magento 1, la table s’appelle core_url_rewrite, et les produits y sont liés par product_id. Au-delà de quelques milliers de lignes, chargez plutôt la liste dans l’extension Redirection.

  11. Basculez, avec un retour arrière prêt. Abaissez le TTL du DNS (la durée pendant laquelle les résolveurs gardent l’enregistrement en cache) à 300 secondes deux jours à l’avance. Le jour venu, mettez Magento en maintenance avec bin/magento maintenance:enable. Importez les commandes et les clients créés depuis votre vidage, synchronisez les stocks, puis déplacez WordPress vers le domaine principal :

    rsync -a ~/new.example.com/ ~/public_html/
    cd ~/public_html
    wp search-replace 'https://new.example.com' 'https://example.com' --all-tables --skip-columns=guid
    wp option get siteurl

    Résultat attendu : un nombre de remplacements, puis https://example.com. Pointez l’enregistrement A du domaine vers le serveur cPanel. Pour revenir en arrière, repointez-le vers le serveur Magento et lancez bin/magento maintenance:disable. Les commandes passées sur WooCommerce entre-temps devront être ressaisies à la main, alors fixez d’avance une date limite de retour arrière.

Vérifier que tout fonctionne

Testez un échantillon aléatoire d’anciennes adresses de produits :

shuf -n 25 ~/product-paths.tsv | cut -f1 | while read -r p; do
  printf '%s  /%s\n' "$(curl -s -o /dev/null -w '%{http_code} %{redirect_url}' "https://example.com/$p")" "$p"
done

Résultat attendu : chaque ligne affiche 301 et une adresse en /product/. Un 404 signale un chemin sans règle. Confirmez ensuite qu’une redirection arrive en un seul saut :

curl -sIL -o /dev/null -w '%{http_code} %{num_redirects} %{url_effective}\n' https://example.com/mh01-hoodie.html

Résultat attendu : 200 1 https://example.com/product/mh01-hoodie/. Enfin, comparez les décomptes du catalogue avec l’étape 1 :

wp post list --post_type=product --format=count
wp post list --post_type=product_variation --format=count

Résultat attendu : le premier nombre égale les produits configurables plus les produits simples vendus seuls, et le second égale les produits simples liés à un parent. Passez ensuite une vraie commande test et confirmez qu’elle arrive dans le tableau de bord de la passerelle.

Dépannage

  • La restauration s’arrête sur une erreur de privilège SUPER. Les déclencheurs de Magento 2 portent un DEFINER qui nomme l’utilisateur de base d’origine, ce qu’un utilisateur cPanel ne peut pas définir. Supprimez et recréez la base, puis restaurez avec gunzip < ~/magento-copy.sql.gz | sed -E 's/DEFINER=[^*]*\*/\*/g' | mysql -u exmpl -p exmpl_mage.
  • Les produits variables n’affichent aucune option. L’attribut a été créé par produit plutôt que globalement. Vérifiez avec wp term list pa_size --fields=name,count. Un résultat vide veut dire que Attribute 1 global manquait, alors supprimez ces produits et refaites les deux passes.
  • Les produits apparaissent deux ou trois fois. Des lignes de vues de magasin ont été importées. Supprimez les doublons et réimportez seulement les lignes où store_view_code est vide. WooCommerce gère un catalogue par site, alors servez les autres langues avec une extension multilingue, ou montez un site WordPress par site Magento.
  • Les redirections renvoient un 404. Le chemin n’a pas de ligne dans le fichier. Vérifiez avec grep -c 'mh01-hoodie.html' ~/public_html/.htaccess, en utilisant le chemin en échec ; 0 veut dire qu’il manque. Les causes habituelles sont un chemin d’une autre vue de magasin, auquel cas relancez l’export avec ce store_id, ou une UGS modifiée à l’import, que vous pouvez chercher dans ~/woo-skus.tsv. Si la ligne est là, vérifiez qu’elle n’a pas été collée dans le bloc # BEGIN WordPress, que l’enregistrement des permaliens efface.

Quand cette approche n’est pas la bonne

Restez sur Magento, ou passez à Mage-OS, la distribution communautaire de Magento Open Source, quand la boutique dépend de fonctions que WooCommerce n’offre pas d’emblée. Cela comprend les outils B2B comme les comptes d’entreprise, les catalogues partagés et les soumissions négociées. Cela comprend aussi plusieurs sites avec leurs propres prix gérés depuis une seule administration, et MSI, l’inventaire multisource de Magento, qui répartit le stock entre plusieurs entrepôts. Les modules sur mesure reliés à un ERP (progiciel de gestion intégré) deviennent chacun une extension à faire écrire de zéro. Un catalogue de dizaines de milliers d’UGS avec de nombreux filtres peut tourner sur WooCommerce, mais il demande un réglage soigné de la base de données et de la recherche.

Magento 1 ne reçoit plus de correctifs officiels depuis juin 2020, alors prévoyez bientôt cette migration ou un passage à Magento 2 ou à OpenMage. Adobe documente l’export de l’administration dans son guide d’export de données, et WooCommerce décrit chaque colonne d’import dans la documentation de l’importateur CSV de produits.

Un WordPress qui rapporte vraiment mérite un opérateur — Stack Harbor opère cette pile sous WordPress Entreprise ; réservez une consultation d’infrastructure.