# Convertir un site Joomla en WordPress : le script qu’on a fini par écrire

> Reprendre un site vitrine Joomla 3 sans plugin d'import générique : un script PHP qui rejoue chaque article et chaque image depuis la base source.

- Auteur : WordPress Développement
- Publié le : 2020-02-07
- Mis à jour le : 2020-02-07
- Catégorie : Outils &amp; workflow
- URL : https://www.wpmoderne.fr/outils/convertir-joomla-wordpress-script/

## L’essentiel

- Lecture directe des tables Joomla via mysqli
- Réutilisation des images sans passer par un export XML
- Script relançable sans doublonner les articles

`SELECT id, title, introtext, fulltext, catid, images FROM jos_content WHERE state = 1;` : cette requête, lancée directement sur la base Joomla, donne accès à l'essentiel d'un site vitrine sans passer par l'interface d'administration. C'est le point de départ du script qui a servi à reprendre un site Joomla 3.9 pour un client dont l'agence d'origine avait disparu, sans documentation ni export propre.

Les extensions génériques d'import Joomla vers WordPress existent, mais elles s'appuient sur un export XML qui échoue régulièrement dès qu'un article contient du HTML mal formé ou des modules imbriqués. Plutôt que de déboguer un format d'échange tiers, il a semblé plus rapide d'écrire un script PHP autonome qui lit la base Joomla en lecture seule et recrée le contenu via les fonctions natives de WordPress.

## Comprendre le schéma Joomla avant d'écrire une ligne de code

Un site Joomla classique en version 3.x stocke ses articles dans la table `jos_content` (le préfixe varie selon l'installation). Les champs utiles sont `title`, `alias`, `introtext` (le chapô), `fulltext` (la suite de l'article), `catid` (la catégorie), `created` et `images`, une colonne JSON qui référence l'image mise en avant et une image intro. Les catégories vivent dans `jos_categories`, avec un champ `title` et un `id` qui fait le lien avec `catid`.

Avant d'écrire le script, un export complet de la base Joomla dans un environnement local isolé s'impose, pour ne jamais interroger la base de production pendant la migration. Une connexion mysqli distincte de `wpdb` permet de lire cette base source sans toucher à la base WordPress cible.

## Rejouer les articles avec les fonctions natives de WordPress

Le script tourne comme une commande WP-CLI personnalisée, ce qui donne accès à `wp_insert_post()`, `wp_insert_attachment()` et `wp_generate_attachment_metadata()` sans réinventer la logique d'insertion. Pour chaque ligne de `jos_content`, le contenu Joomla ( `fulltext` concaténé à `introtext`) passe par un nettoyage minimal des balises obsolètes avant d'être injecté dans `post_content`.

> L'essentiel à retenir : Lecture directe des tables Joomla via mysqli ; Réutilisation des images sans passer par un export XML ; Script relançable sans doublonner les articles

```
foreach ( $joomla_articles as $article ) {
    $existing = get_posts( array(
        'meta_key'   => '_joomla_id',
        'meta_value' => $article['id'],
        'post_type'  => 'post',
        'numberposts' => 1,
    ) );

    if ( ! empty( $existing ) ) {
        continue; // déjà migré, on ne recrée pas de doublon
    }

    $post_id = wp_insert_post( array(
        'post_title'   => $article['title'],
        'post_content' => nettoyer_contenu_joomla( $article['fulltext'] ),
        'post_excerpt' => wp_strip_all_tags( $article['introtext'] ),
        'post_status'  => 'publish',
        'post_date'    => $article['created'],
    ) );

    update_post_meta( $post_id, '_joomla_id', $article['id'] );
}
```

Le champ `_joomla_id` enregistré en meta est ce qui rend le script relançable : en cas d'interruption au bout de 400 articles sur 1 100, on peut le relancer sans créer de doublons, la vérification `get_posts()` filtrant les articles déjà traités.

## Récupérer les images sans export XML

La colonne `images` de Joomla contient un JSON du type `{"image_intro":"images/actus/photo.jpg", ...}`. Le chemin est relatif au dossier `images/` de l'installation Joomla, qu'il suffit de copier tel quel sur le nouveau serveur avant migration. Le script décode ce JSON avec `json_decode()`, vérifie l'existence physique du fichier, puis utilise `wp_upload_bits()` pour copier le binaire dans la structure de médiathèque WordPress avant de créer la pièce jointe avec `wp_insert_attachment()` et `set_post_thumbnail()`.

- Lecture du JSON `images` pour isoler le chemin de l'image intro
- Copie physique du fichier vers `wp-content/uploads` via `wp_upload_bits()`
- Génération des métadonnées avec `wp_generate_attachment_metadata()`
- Association à l'article via `set_post_thumbnail()`

## Les catégories, un mappage à faire à la main

Les catégories Joomla ne correspondent presque jamais telles quelles à une taxonomie WordPress cohérente : elles reflètent souvent l'arborescence du menu plutôt qu'une logique de contenu. Un tableau de correspondance `catid` Joomla vers `term_id` WordPress, construit à la main après une relecture des dix ou vingt catégories du site, évite de reproduire une architecture bancale. La fonction `wp_set_post_terms()` applique ensuite ce mappage lors de l'insertion.

> Ne jamais migrer la structure de menu telle quelle : elle a souvent été pensée pour un back-office Joomla, pas pour une taxonomie WordPress claire.

## Ce que ce script ne traite pas

Les modules Joomla (blocs latéraux, bannières, mini-formulaires) n'ont pas d'équivalent direct dans ce script : ils demandent une analyse séparée, gabarit par gabarit, pour décider s'ils deviennent des widgets, des blocs ou du contenu en dur dans le thème. Mélanger cette conversion avec la migration du contenu éditorial aurait rendu le script beaucoup plus fragile, pour un gain de temps limité sur un site qui n'avait que trois modules actifs.

## En résumé

Un script PHP de quelques dizaines de lignes, exécuté en commande WP-CLI, a suffi pour rejouer plus d'un millier d'articles Joomla vers WordPress sans jamais passer par un format d'export intermédiaire. La clé de la fiabilité tient à deux détails simples : un identifiant Joomla conservé en meta pour rendre le script relançable, et un mappage de catégories construit à la main plutôt qu'automatisé. Pour un site vitrine de taille modeste, cette approche reste plus rapide à déboguer qu'un plugin d'import généraliste.
