📍 Guide principal : Développement WordPress : plugins, thèmes et blocs. Ce tutoriel construit le cœur de données du projet.
Introduction
Un annuaire d’artisans n’est ni un article de blog ni une page : c’est un type de contenu à part entière, avec ses propres champs — un téléphone, un quartier, un métier. WordPress vous laisse inventer ce type et l’administrer comme s’il était natif : même éditeur, même corbeille, même révisions, même présence dans l’API. C’est l’une des capacités les plus puissantes de la plateforme, et celle qui transforme WordPress d’un outil de blog en un véritable système de gestion de contenu sur mesure. À la fin de ce tutoriel, Annuaire Quartier disposera d’un type « artisan » pleinement opérationnel, avec ses champs personnalisés et une classification par métier, le tout visible dans l’éditeur et dans l’API REST.
🎯 Ce que vous allez apprendre
- Déclarer un type de contenu personnalisé avec
register_post_type(). - Comprendre les arguments décisifs :
public,show_in_rest,supports,has_archive. - Attacher des champs personnalisés avec
register_post_meta()et les rendre visibles partout. - Créer une taxonomie « métier » pour classer les artisans.
- Éviter le piège des permaliens en 404 sur un nouveau type.
🛠️ Ce que vous allez construire
Un menu « Artisans » dans l’administration, où l’on saisit des fiches comme on rédige un article. Chaque fiche portera un téléphone et un quartier (des métadonnées), et sera rattachée à un ou plusieurs métiers (une taxonomie). Ces données seront immédiatement disponibles dans l’API REST, ce qui prépare le terrain pour le bloc Gutenberg et l’endpoint des tutoriels suivants.
Prérequis
- L’extension Annuaire Quartier en place (voir l’anatomie d’une extension).
- Les bases des hooks, car tout se branche sur
init. - WordPress 7.0 ou récent,
WP_DEBUGactivé. - Test express : si vous savez écrire un tableau associatif PHP et le passer à une fonction, vous êtes prêt.
- ⏱️ Temps estimé : ~40 minutes.
Étape 1 — Déclarer le type « artisan »
On enregistre un type de contenu avec register_post_type(), toujours sur le hook init. Le premier argument est une clé interne unique — au maximum 20 caractères, préfixée pour éviter les collisions ; on choisit aq_artisan. Le second est un grand tableau d’options qui décrit le comportement du type. Plaçons ce code dans includes/post-types.php.
function aq_register_artisan() {
$labels = array(
'name' => __( 'Artisans', 'annuaire-quartier' ),
'singular_name' => __( 'Artisan', 'annuaire-quartier' ),
'add_new_item' => __( 'Ajouter un artisan', 'annuaire-quartier' ),
'edit_item' => __( 'Modifier l\'artisan', 'annuaire-quartier' ),
'search_items' => __( 'Rechercher un artisan', 'annuaire-quartier' ),
);
register_post_type( 'aq_artisan', array(
'labels' => $labels,
'public' => true,
'show_in_rest' => true,
'menu_icon' => 'dashicons-hammer',
'supports' => array( 'title', 'editor', 'thumbnail' ),
'has_archive' => true,
'rewrite' => array( 'slug' => 'artisans' ),
) );
}
add_action( 'init', 'aq_register_artisan' );
Chaque option compte. public => true rend le type visible côté public et dans l’administration. show_in_rest => true est indispensable : c’est lui qui active l’éditeur de blocs pour ce type et qui l’expose dans l’API REST. L’oublier vous condamne à l’ancien éditeur et coupe l’accès API. supports liste les fonctionnalités de l’éditeur activées — ici le titre, le contenu et l’image mise en avant. has_archive => true crée une page listant tous les artisans, et rewrite fixe le segment d’URL /artisans/.
✅ Point d’étape — Un menu « Artisans » avec une icône de marteau apparaît dans l’administration. Vous pouvez ajouter une fiche, lui donner un titre et un contenu, exactement comme un article.
Étape 2 — Régler le piège des permaliens en 404
Créez une fiche d’artisan, publiez-la, puis cliquez sur « Voir ». Surprise fréquente : une erreur 404. Le contenu existe pourtant. La cause est mécanique : WordPress met en cache ses règles de réécriture d’URL et ne les recalcule pas automatiquement quand un nouveau type apparaît. Il faut les régénérer une fois. Le bon endroit est l’activation de l’extension — c’est exactement pourquoi nous avions ajouté flush_rewrite_rules() dans la routine d’activation au premier tutoriel.
Si votre extension était déjà active avant d’ajouter ce type, la solution la plus simple est d’aller dans Réglages → Permaliens et de cliquer « Enregistrer » sans rien changer : cette action régénère les règles. En production, ne jamais appeler flush_rewrite_rules() sur chaque chargement — c’est coûteux ; on le réserve à l’activation. Ce piège est si courant qu’il vaut la peine de le mémoriser une fois pour toutes : nouveau type ou nouveau slug ⇒ régénérer les permaliens.
Étape 3 — Attacher un premier champ : le téléphone
Un artisan sans numéro de téléphone n’a pas grand intérêt dans un annuaire. On ajoute des métadonnées au type avec register_post_meta(). Déclarer une métadonnée plutôt que de la laisser « libre » apporte trois bénéfices : un typage, un nettoyage automatique des valeurs, et — surtout — une exposition dans l’API REST que le bloc et l’endpoint exploiteront.
function aq_register_meta() {
register_post_meta( 'aq_artisan', 'aq_telephone', array(
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'sanitize_callback' => 'sanitize_text_field',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
},
) );
}
add_action( 'init', 'aq_register_meta' );
Décortiquons. 'single' => true signifie une seule valeur par fiche (et non un tableau). 'show_in_rest' => true rend le champ lisible et modifiable via l’API — sans quoi le bloc Gutenberg ne pourra jamais l’afficher. 'sanitize_callback' nettoie la valeur à l’enregistrement : sanitize_text_field() retire les balises et les espaces superflus. 'auth_callback' décide qui a le droit d’écrire ce champ via l’API ; ici, seuls les utilisateurs pouvant éditer des contenus. Ne jamais laisser une métadonnée exposée en écriture sans contrôle d’accès : ce serait une porte ouverte.
Étape 4 — Ajouter le quartier
Sur le même modèle, ajoutons le quartier. La répétition est volontaire : déclarer chaque champ explicitement vous donne un contrôle total et une intégration native, là où une solution « tout en un » masque ce qui se passe. On enrichit la fonction précédente.
register_post_meta( 'aq_artisan', 'aq_quartier', array(
'type' => 'string',
'single' => true,
'show_in_rest' => true,
'sanitize_callback' => 'sanitize_text_field',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
},
) );
Une fois ce champ déclaré, interrogez l’API à l’adresse /wp-json/wp/v2/aq_artisan pour une fiche existante : les clés aq_telephone et aq_quartier figurent dans l’objet meta de la réponse. C’est la preuve que vos champs sont désormais des citoyens de première classe de WordPress, lisibles par n’importe quel client de l’API.
✅ Point d’étape — Une requête sur
/wp-json/wp/v2/aq_artisanrenvoie vos fiches avec un objetmetacontenantaq_telephoneetaq_quartier. Simetaest vide, c’est queshow_in_restmanque sur la métadonnée.
Étape 5 — Classer les artisans par métier
Le téléphone et le quartier sont des champs libres. Le métier, lui, appartient à une liste fermée et partagée : plombier, couturier, mécanicien, menuisier. Pour ce genre de classification, WordPress fournit les taxonomies — le même mécanisme que les catégories et les étiquettes. On crée une taxonomie « métier » avec register_taxonomy(), rattachée au type « artisan ».
function aq_register_taxonomy() {
register_taxonomy( 'aq_metier', 'aq_artisan', array(
'labels' => array(
'name' => __( 'Métiers', 'annuaire-quartier' ),
'singular_name' => __( 'Métier', 'annuaire-quartier' ),
),
'public' => true,
'show_in_rest' => true,
'hierarchical' => true,
'rewrite' => array( 'slug' => 'metier' ),
) );
}
add_action( 'init', 'aq_register_taxonomy' );
'hierarchical' => true fait fonctionner la taxonomie comme des catégories (cases à cocher, possibilité de parents) plutôt que comme des étiquettes libres. 'show_in_rest' => true l’expose dans l’éditeur et l’API, comme pour le type. Une fois ce code en place, l’écran d’édition d’un artisan affiche un panneau « Métiers » à droite, et vous pouvez y créer plombier, couturier, etc., puis cocher le bon métier pour chaque fiche.
Étape 6 — Saisir l’image et vérifier l’ensemble
Comme nous avons activé thumbnail dans supports, chaque fiche dispose d’un emplacement « Image mise en avant » : on y mettra la photo de la devanture ou du produit. Créez deux ou trois artisans complets — titre, contenu descriptif, téléphone, quartier, métier, image — pour disposer d’un jeu de données réaliste. Ce jeu servira de matière première au bloc Gutenberg et à l’endpoint REST. Un annuaire vide ne permet de tester ni l’un ni l’autre.
✅ Point d’étape — Vous avez plusieurs fiches d’artisans complètes, classées par métier, visibles à la fois dans l’administration et dans l’API. Le cœur de données d’Annuaire Quartier est en place.
Étape 7 — Interroger les artisans en PHP
Vos fiches sont en base, exposées dans l’API. Mais comment les récupérer côté serveur, par exemple pour les afficher dans un gabarit de thème ou les préparer pour un bloc à rendu dynamique ? Avec WP_Query, l’objet de requête central de WordPress. On lui passe le type de contenu visé et, au besoin, un filtre sur la taxonomie. Voici comment récupérer tous les plombiers.
$query = new WP_Query( array(
'post_type' => 'aq_artisan',
'posts_per_page' => 10,
'tax_query' => array(
array(
'taxonomy' => 'aq_metier',
'field' => 'slug',
'terms' => 'plombier',
),
),
) );
if ( $query->have_posts() ) {
while ( $query->have_posts() ) {
$query->the_post();
$tel = get_post_meta( get_the_ID(), 'aq_telephone', true );
// Afficher le titre et le téléphone…
}
wp_reset_postdata();
}
On lit une métadonnée d’une fiche donnée avec get_post_meta( $id, 'aq_telephone', true ) — le troisième argument true demande une valeur unique plutôt qu’un tableau. Le détail le plus oublié est wp_reset_postdata() : après une boucle WP_Query personnalisée, il rétablit le contexte global, sans quoi les fonctions de gabarit appelées ensuite se trompent de contenu. C’est précisément le genre de requête que l’endpoint REST du tutoriel suivant enveloppera pour servir les données à une application externe.
🐞 Pièges fréquents
| Symptôme / erreur | Cause probable | Correctif |
|---|---|---|
| La page d’un artisan renvoie 404 | Permaliens non régénérés | Réenregistrer les permaliens ou appeler flush_rewrite_rules() à l’activation |
| Le type s’ouvre dans l’ancien éditeur | show_in_rest manquant |
Mettre 'show_in_rest' => true dans register_post_type() |
| Les champs n’apparaissent pas dans l’API | show_in_rest oublié sur la métadonnée |
L’ajouter dans register_post_meta() |
Invalid post type |
Clé de plus de 20 caractères | Raccourcir la clé interne (ici aq_artisan) |
| Le type disparaît après changement de thème | Déclaration placée dans le thème | Toujours déclarer les types dans une extension, jamais dans le thème |
✅ Récapitulatif
Vous avez déclaré un type de contenu « artisan » avec register_post_type() en l’activant dans l’API via show_in_rest, ajouté des champs personnalisés avec register_post_meta() en les exposant et en les sécurisant, et créé une taxonomie « métier » avec register_taxonomy(). Vous avez aussi désamorcé le piège des permaliens en 404. Annuaire Quartier possède maintenant un modèle de données complet, prêt à être affiché et exposé dans les tutoriels suivants.
🧾 Aide-mémoire
| Fonction / option | Rôle |
|---|---|
register_post_type( $cle, $args ) |
Déclare un type de contenu (sur init) |
'show_in_rest' => true |
Active l’éditeur de blocs et l’API REST |
'supports' |
Fonctionnalités de l’éditeur (titre, contenu, image…) |
register_post_meta( $type, $cle, $args ) |
Attache un champ personnalisé |
'sanitize_callback' / 'auth_callback' |
Nettoyage et contrôle d’accès du champ |
register_taxonomy( $cle, $type, $args ) |
Crée une classification (métier) |
💪 À vous de jouer
Ajoutez une métadonnée aq_disponible de type booléen indiquant si l’artisan accepte de nouvelles missions, exposée dans l’API. Indice : le type REST d’un booléen se déclare précisément.
Voir une solution
register_post_meta( 'aq_artisan', 'aq_disponible', array(
'type' => 'boolean',
'single' => true,
'show_in_rest' => true,
'default' => true,
'auth_callback'=> function() {
return current_user_can( 'edit_posts' );
},
) );
Le 'type' => 'boolean' et le 'default' => true garantissent une valeur cohérente même pour les fiches créées avant l’ajout du champ.
Tutoriels de la série
- Créer un bloc Gutenberg en React — afficher ces artisans dans l’éditeur.
- L’API REST WordPress — exposer ces fiches à une application externe.
Pour aller plus loin
- 🔝 Retour au guide principal : Développement WordPress : plugins, thèmes et blocs.
- Documentation officielle : Custom Post Types et register_post_meta.
- Tutoriel suivant conseillé : le bloc Gutenberg qui affichera la liste.
FAQ
Faut-il préfixer la clé du type de contenu ?
Fortement recommandé. Une clé non préfixée comme artisan risque d’entrer en conflit avec une autre extension ou un futur type natif. Le préfixe aq_ protège votre déclaration. La clé reste limitée à 20 caractères.
Métadonnée ou taxonomie : comment choisir ?
Une métadonnée convient à une valeur propre à chaque fiche et rarement partagée (un téléphone). Une taxonomie convient à une liste fermée et partagée servant à filtrer et regrouper (un métier). Le métier permet d’afficher « tous les plombiers » d’un clic, ce qu’une métadonnée ne facilite pas.
Pourquoi mes champs n’apparaissent-ils pas dans la barre latérale de l’éditeur ?
Déclarer une métadonnée la rend disponible dans l’API, mais n’ajoute pas automatiquement un champ de saisie dans l’éditeur de blocs. Pour une interface de saisie, on ajoute un panneau dans la barre latérale — ce que permet le développement d’un bloc ou d’une extension d’éditeur, abordé dans le tutoriel sur Gutenberg.
Mes types personnalisés sont-ils sauvegardés lors d’un export ?
Oui, leurs contenus sont inclus dans les exports WordPress standard. En revanche, la déclaration du type vit dans votre extension : sans l’extension active sur le site de destination, les contenus importés restent en base mais ne s’affichent pas. D’où la règle de toujours déclarer les types dans une extension.
Puis-je rendre un type non public, réservé à l’administration ?
Oui, en réglant public sur false et en ajustant les options de visibilité. C’est utile pour des données internes qu’on ne veut pas exposer côté public, par exemple un journal d’événements administratif.