Le WordPress d'aujourd'hui, décodé pour les développeurs

SEO & GEO

Générer un sitemap allégé avec WP_Query et le paramètre fields => ids

Un snippet maison pour construire un sitemap XML sans charger d'objets WP_Post complets, avec un gain de mémoire mesuré sur un vrai catalogue.

Par WordPress Développement • 13 décembre 2023 • 6 min de lecture • Aucun commentaire
Générer un sitemap allégé avec WP_Query et le paramètre fields => ids

'fields' => 'ids'. Trois mots dans un tableau d’arguments, et pourtant ce paramètre change complètement le profil mémoire d’un script de génération de sitemap. Sur un catalogue de pièces détachées comptant plusieurs dizaines de milliers de références, la différence entre une WP_Query classique et une requête restreinte aux identifiants n’est pas cosmétique : elle détermine si le script se termine en quelques secondes ou déclenche une erreur Allowed memory size exhausted.

Ce billet détaille un cas concret : un site e-commerce spécialisé dans les pièces détachées automobiles, dont le sitemap fait maison devait lister l’ensemble des fiches produit sans dépendre d’une extension SEO tierce. La question posée était simple : comment interroger la base sans reconstruire à chaque fois des objets WP_Post complets, avec leur cache de métadonnées, leurs filtres de contenu et leur poids en mémoire ?

Pourquoi une WP_Query classique coûte cher sur un gros catalogue

Par défaut, WP_Query hydrate des objets WP_Post complets : titre, contenu, extrait, statut, auteur, et surtout un luxe de champs que le script n’utilise jamais pour produire un sitemap. Chaque objet occupe de la mémoire, et WordPress met en cache les résultats via update_post_caches(), ce qui ajoute encore de la charge lorsque le nombre de résultats grimpe. Sur un catalogue de 40 000 références, une requête sans restriction peut consommer plusieurs centaines de mégaoctets, un chiffre qui dépasse vite la limite memory_limit configurée sur un hébergement mutualisé ou un plan d’entrée de gamme.

Le sitemap n’a besoin que de trois informations par URL : l’identifiant (ou directement le permalien), la date de dernière modification, et éventuellement la priorité relative. Charger le contenu complet, les métadonnées ACF ou les taxonomies associées pour ensuite les ignorer est un gaspillage évitable.

Le snippet : une requête restreinte aux identifiants

L'essentiel à retenir : Requête allégée sans hydrater les objets post ; Gain mémoire mesuré sur 40 000 références ; Variantes selon le volume du catalogue

Voici la structure de base utilisée pour ce catalogue de pièces détachées. Le type de contenu personnalisé s’appelle piece_detachee, et seules les fiches publiées doivent apparaître dans le sitemap.

function pdr_generer_sitemap_pieces() {
    $args = array(
        'post_type'      => 'piece_detachee',
        'post_status'    => 'publish',
        'fields'         => 'ids',
        'posts_per_page' => 5000,
        'orderby'        => 'ID',
        'order'          => 'ASC',
        'no_found_rows'  => true,
        'update_post_meta_cache' => false,
        'update_post_term_cache' => false,
    );

    $query = new WP_Query( $args );
    $urls  = array();

    foreach ( $query->posts as $post_id ) {
        $urls[] = array(
            'loc'     => get_permalink( $post_id ),
            'lastmod' => get_post_modified_time( 'c', true, $post_id ),
        );
    }

    return $urls;
}

Trois réglages font la différence par rapport à une requête par défaut. D’abord 'fields' => 'ids', qui indique à WordPress de ne renvoyer qu’un tableau d’entiers plutôt que des objets complets. Ensuite 'no_found_rows' => true, qui évite le calcul SQL_CALC_FOUND_ROWS inutile puisque la pagination du sitemap n’est pas traitée ici. Enfin la désactivation du cache de métadonnées et de taxonomies, qui n’a aucun intérêt quand on ne lit ni champ personnalisé ni catégorie.

Ce que révèle la mesure

Sur ce catalogue de 40 000 fiches produit, l’équipe a comparé les deux approches avec memory_get_peak_usage() encadrant l’appel à la requête. La version classique, sans restriction de champs, culminait autour de 210 Mo de mémoire allouée pour parcourir l’ensemble du catalogue par lots de 5 000. La version avec fields => ids et les caches désactivés redescendait à environ 34 Mo pour le même volume, soit un facteur proche de six. Le temps d’exécution suivait la même tendance, avec une requête nettement plus rapide côté MySQL puisque seule la colonne ID est remontée depuis wp_posts.

Cette différence devient déterminante dès que le script tourne dans une tâche planifiée avec un budget mémoire contraint, ou lorsqu’il doit s’exécuter dans le même processus PHP qu’une autre opération lourde, comme un export ou une synchronisation avec un flux fournisseur.

Variantes selon la taille du catalogue

La même logique s’adapte à des volumes différents :

  • Pour un catalogue de quelques centaines de fiches, une seule requête avec 'posts_per_page' => -1 suffit largement, la restriction sur les champs restant recommandée par principe.
  • Pour plusieurs dizaines de milliers de références comme ici, un découpage par lots de 5 000 avec incrémentation de 'offset' ou, mieux, une pagination par identifiant croissant via 'post__in' segmenté évite les pics mémoire ponctuels.
  • Pour un catalogue qui dépasse la limite technique de 50 000 URL par fichier sitemap, cette même requête restreinte sert de brique de base à un découpage en plusieurs fichiers, chacun listant une tranche d’identifiants.

Dans les trois cas, le principe reste identique : ne demander à la base que ce que le sitemap va réellement écrire, rien de plus.

Limites à connaître

Cette optimisation porte sur la construction de la liste d’URL, pas sur la découpe en plusieurs fichiers ni sur la gestion d’un index de sitemaps quand le volume dépasse la limite du protocole. Ces aspects relèvent d’un traitement séparé, une fois la requête de base rendue légère. Il faut aussi noter que 'fields' => 'ids' retourne uniquement des identifiants : toute donnée supplémentaire, comme la date de modification, nécessite un appel dédié tel que get_post_modified_time(), qui reste raisonnable tant qu’il n’ouvre pas à nouveau tout l’objet post en mémoire.

En résumé

Restreindre une WP_Query aux seuls identifiants avec fields => ids, désactiver les caches de métadonnées inutiles et éviter le calcul du nombre total de lignes forment une base simple mais efficace pour tout script de génération de sitemap fait maison. Sur un catalogue conséquent, ce n’est pas un détail d’optimisation : c’est ce qui sépare un script fiable d’un script qui échoue silencieusement à trois heures du matin, au moment où personne ne surveille la tâche planifiée.

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Partager :

À propos de l'auteur

WordPress Développement

Développeur WordPress, passionné par Elementor, le FSE et l’automatisation par IA.

Voir tous ses articles

Dans la même veine

À lire aussi