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

Blocs Gutenberg

HPOS et les blocs WooCommerce : un bloc Panier qui lit le mauvais post meta

Après activation de HPOS, un bloc de panier personnalisé continue d'afficher une remise à zéro. La cause tient à une ligne de lecture de métadonnée jamais mise à jour.

Par WordPress Développement • 29 décembre 2022 • 5 min de lecture • Aucun commentaire
HPOS et les blocs WooCommerce : un bloc Panier qui lit le mauvais post meta

« Remise appliquée : 0,00 € », alors que la commande affiche bien 15 % de réduction dans l’administration WooCommerce. Ce symptôme est apparu chez un client dont le site venait de basculer sur le stockage des commandes en tables personnalisées (HPOS, High-Performance Order Storage), disponible depuis WooCommerce 8.2 en bêta et activable en production depuis le milieu de l’année 2022. Le bloc en cause est un bloc dynamique maison, affiché dans le panier latéral, qui recalcule une remise fidélité à partir d’une métadonnée de commande.

Le code fautif est trivial en apparence : get_post_meta( $order_id, '_loyalty_discount', true ). Il fonctionnait parfaitement avant la bascule. Après activation de HPOS, la fonction renvoie systématiquement une chaîne vide, alors que la même métadonnée est visible et correcte dans l’écran d’édition de la commande. C’est exactement le genre de régression silencieuse qui ne remonte dans aucun journal d’erreurs, puisque PHP ne considère pas cela comme une erreur : la fonction retourne simplement une valeur par défaut.

Comprendre ce que change HPOS pour un bloc

Avant HPOS, une commande WooCommerce est un article comme un autre : un post_type nommé shop_order, stocké dans wp_posts, avec ses métadonnées dans wp_postmeta. Tout code qui appelle get_post_meta() avec l’identifiant de commande fonctionne, puisque cet identifiant est un ID de post classique.

HPOS change ce modèle de stockage. Les commandes sont désormais persistées dans des tables dédiées : une table de commandes, une table de métadonnées de commande, une table d’adresses. L’identifiant de commande n’a plus vocation à correspondre à une ligne de wp_posts — WooCommerce ne crée plus de post de compatibilité par défaut si l’option de synchronisation est désactivée. Résultat : get_post_meta( $order_id, …) interroge une table où la ligne correspondante n’existe pas, ou plus, ou n’est plus tenue à jour.

Où se cache l’appel incriminé

Dans le bloc en question, le rendu passait par un render_callback PHP classique, déclaré dans block.json via l’attribut render. À l’intérieur, la lecture de métadonnée se faisait directement sur l’ID de commande récupéré via WC()->session->get( 'order_awaiting_payment' ), sans jamais passer par l’objet WC_Order.

L'essentiel à retenir : HPOS déplace les commandes hors de wp_posts ; get_post_meta() ne lit plus les bonnes données ; Le correctif tient dans le wrapper de compatibilité

Le correctif : passer par l’objet commande, pas par les fonctions post

La solution WooCommerce officielle consiste à ne plus jamais lire ou écrire une métadonnée de commande via les fonctions get_post_meta() / update_post_meta(), quel que soit le mode de stockage actif. La bonne pratique, documentée par l’équipe WooCommerce, est d’utiliser l’objet WC_Order et ses accesseurs génériques :

$order = wc_get_order( $order_id );

if ( $order instanceof WC_Order ) {
    $discount = $order->get_meta( '_loyalty_discount', true );
} else {
    $discount = '';
}

Ces méthodes fonctionnent indifféremment que HPOS soit activé ou non, puisque WC_Order encapsule la couche de stockage réelle. C’est précisément l’objectif de cette abstraction : permettre aux extensions et aux blocs personnalisés de ne jamais avoir à se soucier de l’emplacement physique des données.

Vérifier la compatibilité déclarée de l’extension

Deuxième point de vigilance : une extension (ou un thème) qui embarque des blocs personnalisés doit déclarer explicitement sa compatibilité avec HPOS, sans quoi WooCommerce peut désactiver le mode table pour l’ensemble du site par précaution. Cette déclaration se fait au chargement du plugin :

add_action( 'before_woocommerce_init', function() {
    if ( class_exists( \Automattic\WooCommerce\Utilities\FeaturesUtil::class ) ) {
        \Automattic\WooCommerce\Utilities\FeaturesUtil::declare_compatibility(
            'custom_order_tables',
            __FILE__,
            true
        );
    }
} );

Sans cette ligne, l’écran WooCommerce → Réglages → Fonctionnalités avancées → Compatibilité des extensions classe le plugin comme « incompatible », ce qui peut bloquer l’activation de HPOS sur l’ensemble de la boutique tant que l’administrateur n’a pas forcé le passage outre.

Une check-list avant toute migration de bloc vers HPOS

  • Recherchez chaque occurrence de get_post_meta() et update_post_meta() ciblant un ID de commande dans le code des blocs.
  • Remplacez ces appels par $order->get_meta() et $order->update_meta_data() suivi de $order->save().
  • Vérifiez les requêtes SQL directes qui joignent wp_postmeta sur un shop_order : elles doivent être réécrites pour cibler wp_wc_orders_meta.
  • Déclarez la compatibilité custom_order_tables même si le plugin ne touche à aucune commande directement.
  • Testez le bloc en environnement de synchronisation activée puis désactivée, pour couvrir les deux scénarios de transition.

Sur ce type de bascule, je préfère toujours activer d’abord le mode « synchronisation » de HPOS, qui maintient un post de compatibilité en parallèle des tables : cela laisse un filet de sécurité le temps d’auditer tous les blocs et intégrations tierces.

En résumé

Ce bug n’a rien d’exotique : il touchera tout code qui a été écrit avant l’existence de HPOS et qui traite un ID de commande comme un ID de post ordinaire. Le réflexe à adopter durablement est simple — ne plus jamais appeler directement les fonctions post pour manipuler une commande WooCommerce, et systématiser l’usage de wc_get_order() couplé aux méthodes de l’objet retourné. C’est un changement d’habitude mineur pour un gain de robustesse considérable face aux futures évolutions du stockage.

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