WordPress 5.6, sorti en décembre 2020, a introduit un hook resté longtemps discret : wp_after_insert_post. Il répond à un problème connu de longue date avec save_post, qui se déclenche parfois plusieurs fois de suite pour une seule action de l’utilisateur, avec des métadonnées pas toujours complètement à jour au moment de l’exécution.
Ce billet montre comment s’appuyer sur ce hook pour invalider proprement un compteur de vues mis en cache et affiché par un bloc dynamique, sans retomber dans les pièges habituels de save_post. Le cache de page complet, souvent posé en complément, relève d’un autre sujet et n’est pas traité ici.
Le problème posé par save_post dans ce contexte
Un bloc dynamique « article populaire » affichait le nombre de vues d’un article, calculé et mis en cache dans un transient renouvelé toutes les heures. Ce compteur devait aussi être invalidé immédiatement après toute modification manuelle du nombre de vues par un administrateur, via un champ personnalisé.
Le hook save_post semblait le candidat naturel, mais il se déclenche à plusieurs moments distincts d’une même sauvegarde (brouillon automatique, sauvegarde manuelle, republication), et les métadonnées ne sont pas toujours garanties complètement écrites au moment précis de son exécution. Résultat observé : le transient était parfois invalidé avant même que la nouvelle valeur du compteur soit disponible, ce qui affichait un chiffre incohérent pendant quelques minutes.
Pourquoi wp_after_insert_post résout ce cas précis
Ce hook se déclenche une seule fois, après que l’article et l’ensemble de ses métadonnées ont été effectivement écrits en base de données, y compris les champs personnalisés associés dans la même requête d’enregistrement. Il reçoit en paramètres l’objet post complet, un indicateur de mise à jour, et le post avant modification, ce qui permet de comparer précisément l’ancienne et la nouvelle valeur du compteur.

add_action( 'wp_after_insert_post', function( $post, $update, $post_before ) {
if ( 'article' !== $post->post_type ) {
return;
}
$ancien_compteur = get_post_meta( $post_before->ID, 'nombre_vues', true );
$nouveau_compteur = get_post_meta( $post->ID, 'nombre_vues', true );
if ( $ancien_compteur !== $nouveau_compteur ) {
delete_transient( 'compteur_vues_' . $post->ID );
}
}, 10, 3 );
Le rendu du bloc dynamique concerné
Le bloc lui-même reste inchangé par cette correction : il continue de lire le transient s’il existe, ou de le recalculer sinon.
function afficher_compteur_vues( $attributs, $contenu, $bloc ) {
$article_id = $attributs['articleId'];
$vues = get_transient( 'compteur_vues_' . $article_id );
if ( false === $vues ) {
$vues = (int) get_post_meta( $article_id, 'nombre_vues', true );
set_transient( 'compteur_vues_' . $article_id, $vues, HOUR_IN_SECONDS );
}
return sprintf( '<span>%d vues</span>', $vues );
}
Les cas à ne pas oublier
- Ignorer les révisions et les brouillons automatiques, non concernés par ce compteur
- Comparer explicitement l’ancienne et la nouvelle valeur, pas seulement la présence du hook
- Vérifier le type de contenu avant tout traitement, pour éviter de déclencher la logique sur des pages ou d’autres types de contenu
Une variante utile : recalculer par lot
Sur un site avec plusieurs blocs de compteurs affichés côte à côte, il peut être utile d’invalider aussi les transients des articles liés (par exemple une série d’articles du même auteur), en parcourant une liste restreinte d’identifiants plutôt que tout le site, pour éviter une invalidation trop large et coûteuse en requêtes.
Sur ce type d’invalidation ciblée, on préfère toujours comparer explicitement l’avant et l’après plutôt que de supprimer le cache à chaque déclenchement du hook, même quand rien n’a réellement changé.
En résumé
wp_after_insert_post reste sous-utilisé alors qu’il résout précisément les cas où save_post montre ses limites : invalidation de cache liée à une métadonnée, traitement qui dépend de données garanties complètes. Pour un bloc dynamique qui affiche une donnée mise en cache, c’est souvent le hook le plus fiable disponible dans le cœur de WordPress.