« La catégorie existe bien dans l’administration, mais elle n’apparaît nulle part sur le site » : ce constat revient régulièrement dès qu’un terme de taxonomie vient d’être créé sans qu’aucun contenu ne lui soit encore rattaché.
La cause tient à un paramètre par défaut de get_terms(), la fonction native de WordPress pour interroger les termes d’une ou plusieurs taxonomies : hide_empty, qui vaut true tant qu’on ne le précise pas explicitement dans les arguments de la requête.
Ce que hide_empty filtre réellement
Quand hide_empty vaut true, get_terms() exclut du résultat tout terme qui n’est associé à aucun contenu publié dans la taxonomie interrogée. Ce comportement a un sens pratique dans bien des contextes : personne ne souhaite afficher, dans un nuage de catégories en pied de page, une entrée qui ne mène vers aucun article.
Le problème apparaît dans des scénarios différents : une nouvelle catégorie vient d’être créée en prévision d’un contenu à venir, une taxonomie sert à structurer une donnée qui n’est pas systématiquement rattachée à un article publié, ou une interface d’administration doit lister l’ensemble des termes existants, y compris ceux qui ne servent encore à rien.
Basculer hide_empty à false

La correction est directe : passer explicitement hide_empty à false dans les arguments transmis à get_terms() :
$categories = get_terms( array(
'taxonomy' => 'category',
'hide_empty' => false,
) );
foreach ( $categories as $categorie ) {
echo esc_html( $categorie->name ) . ' (' . (int) $categorie->count . ")\n";
}
Le champ count de chaque objet WP_Term renvoyé reste disponible quel que soit le réglage de hide_empty : il indique simplement le nombre de contenus rattachés, y compris zéro pour un terme encore vide. Ce compteur permet, si besoin, de distinguer visuellement les termes actifs des termes en attente de contenu, sans pour autant les masquer complètement.
Le même paramètre dans une boucle d’articles
Une confusion fréquente consiste à croire que hide_empty n’existe que dans get_terms(). Le même paramètre existe aussi dans tax_query au sein de WP_Query, mais avec un rôle différent : il ne filtre pas les articles eux-mêmes, il concerne uniquement la génération de listes de termes annexes, par exemple via wp_list_categories() ou wp_tag_cloud(), deux fonctions qui reposent en interne sur get_terms() et héritent donc du même comportement par défaut.
wp_list_categories()masque par défaut les catégories vides, pour les mêmes raisons queget_terms().wp_tag_cloud()applique la même logique sur les étiquettes.- Un appel direct à
get_terms()dans du code personnalisé hérite du même réglage tant qu’il n’est pas modifié.
Un cas d’usage : une taxonomie de statut interne
Sur un projet qui utilise une taxonomie personnalisée pour classer des dossiers par statut — « en cours », « archivé », « en attente de validation » — certains statuts peuvent légitimement ne contenir aucun élément à un instant donné. Une interface d’administration qui doit proposer un sélecteur avec tous les statuts possibles, même ceux actuellement vides, doit impérativement passer hide_empty à false, sous peine de faire disparaître des options du formulaire selon l’état courant des données.
$statuts = get_terms( array(
'taxonomy' => 'statut_dossier',
'hide_empty' => false,
'orderby' => 'term_order',
) );
Attention à l’effet de bord sur le comptage
Un point à surveiller : le champ count ne comptabilise que les contenus dans un statut public par défaut. Un terme rattaché uniquement à des brouillons peut ainsi apparaître comme vide (count à zéro) même si des contenus lui sont bien associés, simplement parce qu’ils ne sont pas encore publiés. Ce détail explique certaines disparitions de termes qui semblent pourtant utilisés, une fois qu’on regarde du côté des statuts de publication plutôt que du seul comptage brut.
Sur toute interface d’administration qui doit lister des termes de façon exhaustive, vérifier explicitement la valeur de
hide_emptyévite bien des « catégories fantômes » signalées par un client perplexe.
En résumé
hide_empty rend service dans un affichage public destiné aux visiteurs, mais devient un piège dès qu’une interface a besoin de la liste complète des termes existants, y compris ceux encore vides. Le réflexe à garder : se demander, à chaque appel de get_terms(), si le contexte exige réellement de masquer les termes sans contenu, plutôt que de laisser filer le comportement par défaut sans y penser.