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

Blocs Gutenberg

Antipatterns de blocs en 2020 : la logique métier rangée dans save()

Repérer pourquoi une logique conditionnelle écrite dans save() casse à la première mise à jour, contrairement à un rendu dynamique côté serveur.

Par WordPress Développement • 5 janvier 2020 • 4 min de lecture • Aucun commentaire
Antipatterns de blocs en 2020 : la logique métier rangée dans save()

« Ce bloc marchait très bien la semaine dernière, et maintenant tous les articles affichent un bandeau d’erreur. » Cette phrase, entendue sur plusieurs projets au tournant de l’année, désigne presque toujours la même cause : une logique conditionnelle glissée dans la fonction save() d’un bloc, plutôt que dans son rendu côté serveur.

Ce billet décrit cet antipattern précis, pourquoi il casse au moindre changement, et comment le remplacer par un rendu dynamique. Les blocs purement statiques, sans aucune logique conditionnelle, ne sont pas concernés par ce problème et restent hors sujet ici.

Ce qu’on voit sur le terrain

Un bloc « offre spéciale » a été développé pour afficher un bandeau différent selon que la date de fin de promotion est dépassée ou non. La tentation, très répandue chez qui découvre l’API des blocs, consiste à écrire cette condition directement dans la fonction save() :

save( { attributes } ) {
    const expire = new Date( attributes.dateFin ) < new Date();
    return (
        <div className={ expire ? 'offre-expiree' : 'offre-active' }>
            { attributes.texte }
        </div>
    );
}

Ce code compile sans erreur, s’affiche correctement à l’écriture de l’article, et pose problème uniquement plus tard, silencieusement.

Pourquoi c’est un problème

La fonction save() ne s’exécute qu’une seule fois, au moment de l’enregistrement de l’article dans l’éditeur. Son résultat est figé dans le contenu HTML stocké en base, sous forme de commentaires de bloc. À la prochaine ouverture de l’article, WordPress compare le balisage attendu (recalculé à partir du code actuel) au balisage stocké : si expire a changé de valeur entre-temps, la comparaison échoue, et l’article passe en « contenu invalide », avec le fameux écran de récupération de bloc.

Autrement dit, une valeur calculée à un instant T (ici une date comparée à « maintenant ») ne peut jamais rester stable dans save(), puisque « maintenant » change à chaque ouverture de la page. C’est le cœur du problème : save() doit produire un résultat déterministe, toujours identique pour les mêmes attributs, jamais dépendant de l’heure d’exécution.

L'essentiel à retenir : save() ne doit produire que du balisage figé, jamais de logique ; Toute condition métier appartient au render côté serveur ; Une modification de save() invalide tout le contenu existant

Ce qu’il faut faire à la place

La bonne approche consiste à déclarer le bloc comme dynamique, avec une fonction save() qui ne renvoie rien (return null), et à déplacer toute la logique conditionnelle dans un render_callback PHP, exécuté à chaque affichage de la page :

save() {
    return null;
}

// côté PHP
function rendre_offre_speciale( $attributs ) {
    $expire = strtotime( $attributs['dateFin'] ) < time();
    $classe = $expire ? 'offre-expiree' : 'offre-active';
    return sprintf( '<div class="%s">%s</div>', esc_attr( $classe ), esc_html( $attributs['texte'] ) );
}

Ce déplacement change tout : la date n’est plus jamais comparée qu’au moment réel de l’affichage de la page, jamais figée dans le contenu stocké. Le bloc reste valide indéfiniment, quelle que soit la date à laquelle un visiteur consulte la page.

Repérer ce piège avant qu’il ne coûte cher

  • Toute comparaison à new Date() dans save() est un signal d’alarme immédiat
  • Tout accès à une donnée externe (compteur, stock, statut) dans save() doit être banni
  • Si save() contient une condition if qui dépend d’autre chose que des attributs eux-mêmes, la logique est mal placée

Le test qui aurait tout révélé

Un test simple aurait suffi à détecter ce problème avant la mise en production : publier un article avec une date de fin proche, puis rouvrir cet article après l’échéance. Sur un bloc bien conçu, aucun changement d’état ne doit jamais rendre le contenu stocké invalide.

Une règle qu’on applique systématiquement en revue de code : si une fonction save() contient autre chose qu’un rendu déterministe des attributs, quelque chose ne va pas.

Quoi faire

Toute logique métier, aussi simple soit-elle, appartient au rendu côté serveur d’un bloc dynamique, jamais à sa fonction save(). Ce réflexe, une fois acquis, évite la quasi-totalité des écrans de récupération de bloc rencontrés sur des blocs personnalisés en début de projet.

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