# get_field en boucle : comment ACF multiplie les requêtes sans cache de champs

> Une boucle de 50 articles qui appelle get_field() pour chaque champ répété déclenche des centaines de requêtes. La solution passe par update_meta_cache.

- Auteur : Clément Hadrot
- Publié le : 2021-05-11
- Mis à jour le : 2021-05-11
- Catégorie : Performance
- URL : https://wpmoderne.dev.wordpress-developpement.fr/performance/get-field-boucle-acf-cache-champs/

## L’essentiel

- get_field() sans précharge déclenche une requête par champ et par article
- update_meta_cache précharge tous les postmeta d'un lot en une requête
- Le gain grandit avec le nombre d'articles et de champs affichés

« La page catégorie met presque trois secondes à s'afficher, mais elle n'a rien de spécial. » C'est avec cette remarque qu'un client a signalé un ralentissement sur son site d'annuaire professionnel, dont chaque fiche affichait cinq champs ACF : logo, ville, spécialité, note moyenne et lien vers le site externe. La page catégorie affichait cinquante fiches par page, dans une boucle `WP_Query` classique.

L'onglet « Requêtes » de Query Monitor a immédiatement montré le problème : plus de 250 requêtes SQL pour cette seule page, la quasi-totalité concernant la table `wp_postmeta`, avec un motif répétitif qui trahissait un défaut classique d'utilisation d'Advanced Custom Fields.

## Pourquoi get_field() multiplie les requêtes

La fonction `get_field()` s'appuie en interne sur `get_post_meta()`, qui lit la table `wp_postmeta`. WordPress dispose d'un mécanisme de cache des métadonnées par article, mais ce cache ne se remplit que pour les articles dont les métadonnées ont déjà été chargées en une fois, typiquement via la boucle principale de `WP_Query` lorsque l'option `update_post_meta_cache` est active, ce qui est le cas par défaut.

Le vrai problème sur ce projet venait d'ailleurs : la boucle d'affichage des fiches n'utilisait pas directement les résultats de la requête principale, mais rappelait `get_posts()` à l'intérieur de la boucle pour récupérer des informations complémentaires liées à chaque professionnel, ce qui cassait le préchargement natif et forçait une lecture individuelle des métadonnées pour chaque champ de chaque fiche.

## Le rôle d'update_meta_cache

La fonction `update_meta_cache()`, appelée avec le type de méta concerné et un tableau d'identifiants d'articles, permet de précharger en une seule requête SQL l'ensemble des métadonnées de plusieurs articles à la fois, qui sont ensuite servies depuis le cache d'objet à chaque appel de `get_field()` ou `get_post_meta()` :

> L'essentiel à retenir : get_field() sans précharge déclenche une requête par champ et par article ; update_meta_cache précharge tous les postmeta d'un lot en une requête ; Le gain grandit avec le nombre d'articles et de champs affichés

```
$ids = wp_list_pluck( $query->posts, 'ID' );
update_meta_cache( 'post', $ids );

foreach ( $query->posts as $post ) {
    $ville      = get_field( 'ville', $post->ID );
    $specialite = get_field( 'specialite', $post->ID );
    $note       = get_field( 'note_moyenne', $post->ID );
}
```

Avec ce préchargement explicite, ajouté juste après la récupération des résultats de la requête principale, les 250 requêtes constatées initialement sont tombées à une dizaine, la majeure partie du temps de chargement de la page se déplaçant vers le rendu HTML lui-même plutôt que vers la base de données.

## Le cas particulier des appels get_posts() imbriqués

Le vrai correctif de fond a consisté à supprimer l'appel `get_posts()` imbriqué dans la boucle, qui récupérait un article lié pour afficher son titre. Cet appel a été remplacé par une jointure logique effectuée en amont, avant la boucle d'affichage, en récupérant tous les identifiants liés en une seule requête via `WP_Query` avec le paramètre `post__in`, puis en construisant un tableau associatif en mémoire :

```
$ids_lies = array_filter( array_map( function ( $post ) {
    return get_field( 'article_lie', $post->ID, false );
}, $query->posts ) );

$articles_lies = get_posts( array(
    'post__in'       => $ids_lies,
    'posts_per_page' => -1,
    'post_type'      => 'any',
) );
$titres_par_id = wp_list_pluck( $articles_lies, 'post_title', 'ID' );
```

Ce genre de restructuration demande un peu plus de code au moment de l'écriture, mais élimine la source la plus coûteuse de requêtes N+1 dans une boucle d'affichage.

## Ce que cette optimisation ne couvre pas

Cette approche traite le préchargement des champs simples de type texte, nombre ou sélection. Les champs de relation ACF, qui pointent eux-mêmes vers d'autres articles avec leurs propres métadonnées, posent un problème de N+1 supplémentaire qui demande une stratégie de préchargement différente, non abordée ici.

## En résumé

Un simple `update_meta_cache()` placé juste après la requête principale, combiné à la suppression des appels `get_posts()` imbriqués dans une boucle, a fait passer le temps de génération de la page catégorie de près de trois secondes à moins de 400 millisecondes côté serveur. C'est l'un des correctifs de performance ACF les plus rentables à mettre en œuvre, pour un coût de développement minime.
