WordPress

Guide complet : Les Custom Post Types WordPress

14 min de lecture

Les Custom Post Types WordPress : au-delà des articles et pages

Par défaut, WordPress propose 2 types de contenu : les articles (blog) et les pages (contenu statique). Mais si vous gérez des biens immobiliers, un catalogue de produits, des témoignages, des événements ou un portfolio, vous avez besoin de types de contenu personnalisés (Custom Post Types — CPT). Les CPT sont la fonctionnalité qui transforme WordPress d’un simple blog en un véritable CMS capable de gérer n’importe quel type de données.

1. Quand créer un Custom Post Type

Créez un CPT quand votre contenu :

  • A une structure différente des articles classiques (champs spécifiques)
  • Doit être filtré et recherché indépendamment
  • A besoin de son propre template d’affichage
  • N’entre pas naturellement dans les catégories du blog

Exemples concrets

Type de site CPT recommandés
Agence immobilière Biens, Agents, Témoignages
Restaurant Menu (plats), Événements, Réservations
Agence web Portfolio, Services, Équipe, Témoignages
École/Formation Cours, Formateurs, Sessions
ONG Projets, Rapports, Partenaires
E-commerce (hors WooCommerce) Produits, Commandes, Avis

2. Créer un CPT manuellement (functions.php)

Voici un exemple complet pour un CPT « Service » destiné à une agence web sénégalaise :

// Enregistrer le CPT "Service"
add_action('init', function() {
    register_post_type('service', [
        'labels' => [
            'name'               => 'Services',
            'singular_name'      => 'Service',
            'add_new'            => 'Ajouter un service',
            'add_new_item'       => 'Ajouter un nouveau service',
            'edit_item'          => 'Modifier le service',
            'new_item'           => 'Nouveau service',
            'view_item'          => 'Voir le service',
            'search_items'       => 'Rechercher un service',
            'not_found'          => 'Aucun service trouvé',
            'not_found_in_trash' => 'Aucun service dans la corbeille',
            'all_items'          => 'Tous les services',
            'menu_name'          => 'Services',
        ],
        'public'             => true,
        'has_archive'        => true,
        'rewrite'            => ['slug' => 'services', 'with_front' => false],
        'supports'           => ['title', 'editor', 'thumbnail', 'excerpt', 'page-attributes'],
        'menu_icon'          => 'dashicons-portfolio',
        'menu_position'      => 5,
        'show_in_rest'       => true, // Active Gutenberg et l'API REST
        'publicly_queryable' => true,
        'capability_type'    => 'post',
    ]);
});

// IMPORTANT : après avoir ajouté ce code, allez dans
// Réglages → Permaliens et cliquez "Enregistrer"
// pour régénérer les règles de réécriture

Explication des paramètres clés

Paramètre Valeur Effet
public true Visible sur le site et dans l’admin
has_archive true Crée une page d’archive à /services/
rewrite [‘slug’ => ‘services’] URL : votresite.com/services/nom-du-service
supports tableau Fonctionnalités de l’éditeur (titre, contenu, image…)
show_in_rest true Compatible Gutenberg et API REST
menu_icon dashicons-* Icône dans le menu admin

3. Taxonomies personnalisées

Les taxonomies organisent vos CPT comme les catégories et tags organisent les articles :

// Taxonomie hiérarchique (comme les catégories)
add_action('init', function() {
    register_taxonomy('type_service', 'service', [
        'labels' => [
            'name'          => 'Types de service',
            'singular_name' => 'Type de service',
            'search_items'  => 'Rechercher un type',
            'all_items'     => 'Tous les types',
            'edit_item'     => 'Modifier le type',
            'add_new_item'  => 'Ajouter un type',
        ],
        'hierarchical' => true,  // true = catégories, false = tags
        'rewrite'      => ['slug' => 'type-service'],
        'show_in_rest' => true,
        'show_admin_column' => true, // Colonne dans la liste admin
    ]);
    
    // Ajouter des termes par défaut
    $types = ['Développement web', 'Design graphique', 'SEO', 
              'Marketing digital', 'Formation', 'Maintenance'];
    foreach ($types as $type) {
        if (!term_exists($type, 'type_service')) {
            wp_insert_term($type, 'type_service');
        }
    }
});

// Taxonomie plate (comme les tags)
add_action('init', function() {
    register_taxonomy('technologie', 'service', [
        'labels' => [
            'name'          => 'Technologies',
            'singular_name' => 'Technologie',
        ],
        'hierarchical' => false,
        'rewrite'      => ['slug' => 'tech'],
        'show_in_rest' => true,
    ]);
});

4. Champs personnalisés avec ACF

Les CPT deviennent vraiment puissants avec des champs personnalisés. Advanced Custom Fields (ACF) est l’outil standard :

  1. Installez ACF depuis Extensions → Ajouter
  2. Allez dans ACF → Groupes de champs → Ajouter
  3. Créez un groupe « Détails du service »
  4. Condition d’affichage : « Type de publication = Service »

Champs recommandés pour un CPT « Service »

Champ Type ACF Usage
Prix à partir de Number Montant en FCFA
Durée estimée Text « 2-4 semaines »
Icône Select ou Image Icône visuelle du service
Points clés Repeater Liste de bénéfices
Portfolio associé Relationship Lier aux réalisations
CTA texte Text « Demander un devis »
CTA lien URL Lien WhatsApp ou formulaire

Afficher les champs ACF dans le template

// Dans single-service.php
<?php get_header(); ?>

<article class="service-detail">
  <div class="service-detail__hero">
    <?php if (has_post_thumbnail()) : ?>
      <img src="<?php the_post_thumbnail_url('large'); ?>" 
        alt="<?php the_title_attribute(); ?>" class="service-detail__image">
    <?php endif; ?>
    
    <div class="service-detail__info">
      <h1><?php the_title(); ?></h1>
      
      <?php if ($prix = get_field('prix')) : ?>
        <p class="service-price">
          À partir de <strong><?php echo number_format($prix, 0, ',', ' '); ?> FCFA</strong>
        </p>
      <?php endif; ?>
      
      <?php if ($duree = get_field('duree_estimee')) : ?>
        <p class="service-duration">Durée : <?php echo esc_html($duree); ?></p>
      <?php endif; ?>
    </div>
  </div>
  
  <div class="service-detail__content">
    <?php the_content(); ?>
  </div>
  
  <!-- Points clés (champ Repeater) -->
  <?php if (have_rows('points_cles')) : ?>
    <div class="service-features">
      <h2>Ce qui est inclus</h2>
      <ul>
        <?php while (have_rows('points_cles')) : the_row(); ?>
          <li><?php the_sub_field('point'); ?></li>
        <?php endwhile; ?>
      </ul>
    </div>
  <?php endif; ?>
  
  <!-- CTA -->
  <?php $cta_link = get_field('cta_lien'); ?>
  <?php if ($cta_link) : ?>
    <div class="service-cta">
      <a href="<?php echo esc_url($cta_link); ?>" class="btn-primary">
        <?php echo esc_html(get_field('cta_texte') ?: 'Demander un devis'); ?>
      </a>
    </div>
  <?php endif; ?>
  
  <!-- Réalisations associées -->
  <?php $portfolio = get_field('portfolio_associe'); ?>
  <?php if ($portfolio) : ?>
    <div class="service-portfolio">
      <h2>Réalisations dans ce domaine</h2>
      <div class="portfolio-grid">
        <?php foreach ($portfolio as $project) : ?>
          <a href="<?php echo get_permalink($project->ID); ?>" class="portfolio-item">
            <?php echo get_the_post_thumbnail($project->ID, 'medium'); ?>
            <h3><?php echo esc_html($project->post_title); ?></h3>
          </a>
        <?php endforeach; ?>
      </div>
    </div>
  <?php endif; ?>
</article>

<?php get_footer(); ?>

5. Créer un CPT sans code : plugins

Custom Post Type UI (CPT UI)

Si vous ne voulez pas toucher au code, CPT UI (gratuit) offre une interface pour créer des CPT et taxonomies :

  1. Installez CPT UI depuis Extensions → Ajouter
  2. CPT UI → Ajouter un type de publication
  3. Remplissez le slug, les labels et les options
  4. Cliquez « Ajouter le type de publication »

Avantage : pas de code. Limite : si vous désactivez le plugin, les CPT disparaissent du menu (les données restent en base). Le code dans functions.php est plus durable.

6. Templates de CPT

WordPress utilise la hiérarchie de templates pour afficher les CPT :

// Page individuelle d'un service :
single-service.php      → Template spécifique
single.php              → Fallback générique

// Archive (liste de tous les services) :
archive-service.php     → Template spécifique
archive.php             → Fallback générique

// Taxonomie :
taxonomy-type_service.php  → Template par taxonomie
taxonomy.php               → Fallback
archive.php                → Fallback générique

Archive personnalisée (archive-service.php)

<?php get_header(); ?>

<section class="services-archive">
  <h1>Nos Services</h1>
  
  <!-- Filtres par type -->
  <div class="service-filters">
    <a href="<?php echo get_post_type_archive_link('service'); ?>" 
      class="filter-btn active">Tous</a>
    <?php 
    $types = get_terms(['taxonomy' => 'type_service', 'hide_empty' => true]);
    foreach ($types as $type) : ?>
      <a href="<?php echo get_term_link($type); ?>" 
        class="filter-btn"><?php echo esc_html($type->name); ?></a>
    <?php endforeach; ?>
  </div>
  
  <div class="services-grid">
    <?php while (have_posts()) : the_post(); ?>
      <div class="service-card">
        <?php if (has_post_thumbnail()) : ?>
          <img src="<?php the_post_thumbnail_url('medium'); ?>" 
            alt="<?php the_title_attribute(); ?>" loading="lazy">
        <?php endif; ?>
        <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
        <p><?php the_excerpt(); ?></p>
        <?php if ($prix = get_field('prix')) : ?>
          <span class="price">À partir de <?php echo number_format($prix, 0, ',', ' '); ?> FCFA</span>
        <?php endif; ?>
        <a href="<?php the_permalink(); ?>" class="btn-secondary">En savoir plus →</a>
      </div>
    <?php endwhile; ?>
  </div>
  
  <?php the_posts_pagination(); ?>
</section>

<?php get_footer(); ?>

7. Requêtes WP_Query sur les CPT

// Afficher les 6 derniers services sur la page d'accueil
$services = new WP_Query([
    'post_type'      => 'service',
    'posts_per_page' => 6,
    'orderby'        => 'menu_order',
    'order'          => 'ASC',
]);

if ($services->have_posts()) :
    while ($services->have_posts()) : $services->the_post();
        // Afficher chaque service
    endwhile;
    wp_reset_postdata();
endif;

// Filtrer par taxonomie
$services_dev = new WP_Query([
    'post_type' => 'service',
    'tax_query' => [
        [
            'taxonomy' => 'type_service',
            'field'    => 'slug',
            'terms'    => 'developpement-web',
        ],
    ],
]);

// Filtrer par champ ACF (meta_query)
$services_abordables = new WP_Query([
    'post_type'  => 'service',
    'meta_query' => [
        [
            'key'     => 'prix',
            'value'   => 100000,
            'compare' => '<=',
            'type'    => 'NUMERIC',
        ],
    ],
    'orderby'  => 'meta_value_num',
    'meta_key' => 'prix',
    'order'    => 'ASC',
]);

8. Exposer le CPT dans l’API REST

// Avec show_in_rest => true dans register_post_type,
// votre CPT est accessible via l'API REST :

// GET tous les services :
// https://votresite.com/wp-json/wp/v2/service

// GET un service spécifique :
// https://votresite.com/wp-json/wp/v2/service/123

// Exposer les champs ACF dans l'API REST :
add_action('rest_api_init', function() {
    register_rest_field('service', 'prix', [
        'get_callback' => function($post) {
            return get_field('prix', $post['id']);
        },
    ]);
    
    register_rest_field('service', 'duree', [
        'get_callback' => function($post) {
            return get_field('duree_estimee', $post['id']);
        },
    ]);
});

9. Bonnes pratiques CPT

  • Nommage : le slug du CPT doit être court, en minuscules, sans accents (ex: service, pas nos-services-professionnels)
  • Pas de conflit : évitez les slugs réservés par WordPress : post, page, attachment, revision, nav_menu_item
  • Flush des permaliens : après chaque ajout/modification de CPT, allez dans Réglages → Permaliens et sauvegardez
  • Préfixez vos CPT : utilisez un préfixe pour éviter les conflits avec les plugins (ex: itsc_service)
  • Code dans un plugin : placez le code CPT dans un plugin (mu-plugin) plutôt que dans functions.php — cela dissocie les données du thème. Si vous changez de thème, vos CPT restent
// Créer un mu-plugin : wp-content/mu-plugins/custom-post-types.php
<?php
/**
 * Plugin Name: Custom Post Types
 * Description: CPT personnalisés pour le site
 */

// Tout le code register_post_type et register_taxonomy ici
// Les mu-plugins sont chargés automatiquement, avant les autres plugins

Les Custom Post Types sont la fonctionnalité la plus puissante de WordPress pour créer des sites sur mesure. Combinés avec ACF pour les champs personnalisés et des templates dédiés, ils permettent de gérer n’importe quel type de contenu structuré sans aucun plugin lourd.

Faire concevoir un site web professionnel

Site vitrine ou e-commerce, livré clé en main avec domaine, hébergement, formation et support inclus.

À partir de 350 000 FCFA

📧 E-mail
💬 WhatsApp

Pourquoi les Custom Post Types changent tout

WordPress livre par defaut deux types de contenu : Articles (post) et Pages (page). Cette dualite suffit pour un blog, pas pour un site qui gere des donnees structurees. Les Custom Post Types (CPT) permettent de creer ses propres types : Produits, Evenements, Annonces, Temoignages, Portfolio, Recettes, Equipe, Realisations. Chaque CPT a son propre menu, ses propres champs, ses propres permaliens, sa propre archive, ses propres permissions.

Pour comprendre l importance : WooCommerce est techniquement un ensemble de CPT (Product) avec des taxonomies (Categories produit, Etiquettes produit). Toute extension WordPress serieuse qui gere des donnees metier utilise les CPT. Les maitriser est la frontiere entre un developpeur WordPress de surface et un developpeur qui peut construire des applications metier.

Enregistrer un CPT — l API register_post_type

La fonction register_post_type de WordPress accepte un nom et un tableau d arguments qui controlent tout le comportement. Les arguments cles : labels (textes affiches dans l admin), public (apparait sur le front et dans les recherches), has_archive (URL archive automatique), menu_icon, supports (title, editor, thumbnail, excerpt, custom-fields, revisions, page-attributes), capability_type (permissions), rewrite (structure d URL).

Le code minimal pour un CPT Evenement :

add_action(  init  , function () {    register_post_type(  evenement  , array(         labels  => array(             name  =>  Evenements ,             singular_name  =>  Evenement ,        ),         public  => true,         has_archive  => true,         menu_icon  =>  dashicons-calendar-alt ,         supports  => array(  title ,  editor ,  thumbnail ,  excerpt ),         rewrite  => array(  slug  =>  evenements ),         show_in_rest  => true,    ));});

Apres ce code, le menu Evenements apparait dans l admin. Les URL publiques deviennent /evenements/mon-evenement/ et l archive /evenements/. La cle show_in_rest: true est critique en 2026 : sans elle, le CPT n est pas accessible via REST API ni dans l editeur Gutenberg.

Les taxonomies custom pour categoriser

Apres un CPT, on enregistre presque toujours une ou deux taxonomies pour le classer. Pour les Evenements : Categorie d evenement (Sport, Culture, Conference) et Lieu (Dakar, Abidjan, Bamako). La fonction register_taxonomy :

register_taxonomy(  evenement_categorie ,  evenement , array(     labels  => array(  name  =>  Categories ),     hierarchical  => true,  // comme Categorie     show_in_rest  => true,     rewrite  => array(  slug  =>  evenements-categorie ),));

Le parametre hierarchical distingue les taxonomies de type Categorie (avec arborescence parent/enfant) des taxonomies de type Etiquette (a plat). Pour un Lieu, hierarchical = true (Pays → Region → Ville). Pour des tags libres, hierarchical = false.

Custom Fields — au-dela du titre et de l editeur

Un Evenement a typiquement plus que un titre et un contenu : date de debut, date de fin, lieu precis, prix, capacite, lien d inscription, photo de l intervenant. Ces champs ne sont pas dans l editeur Gutenberg de base — il faut les ajouter comme custom fields.

Trois approches en 2026. Advanced Custom Fields (ACF), racheté par WP Engine en 2022, reste le standard de fait. Interface intuitive, plus de 30 types de champs (date, image, repeater, gallery, relationship, post-object). Plan gratuit suffisant pour la majorite des cas, plan Pro (50 USD/an) pour Flexible Content, Repeater, Options Pages.

Meta Box, alternative tres populaire, plus orientee developpeur (definition par code PHP par defaut, GUI optionnel). Tres rapide en execution et flexible. License lifetime competitive (~150 USD).

Block Bindings et bloc Group avec attributs dynamiques en WordPress 6.5+. Approche native qui evite les dependances. Plus de boilerplate mais zero dependance plugin. Cible : projets long terme qui veulent rester sur le core WordPress.

Templates dedies au CPT

WordPress cherche les templates dans cet ordre pour afficher un Evenement : single-evenement.php (template specifique au CPT), single.php (template generique article), singular.php (template generique singulier), index.php (fallback ultime). Pour personnaliser le rendu d un Evenement (afficher la date, le lieu, le prix), creer single-evenement.php dans le theme.

Pour l archive : archive-evenement.php est utilise. Permet d afficher la liste des evenements avec un layout adapte (cartes en grille, filtres par categorie, calendrier).

FAQ

Faut-il un plugin pour creer un CPT ou peut-on le coder a la main ?
Les deux marchent. Plugin (Custom Post Type UI, par exemple) pour un client non-technique qui veut autonomie sur la creation de nouveaux types. Code dans un mu-plugin pour un developpeur qui versione le code en Git et garde le controle. La pratique professionnelle privilegie le code, plus reproductible et plus performant.

Combien de CPT peut-on creer sur un site ?
Pas de limite technique stricte, mais au-dela de 10-15 CPT, l admin devient confus pour les utilisateurs. Si les besoins depassent ce seuil, repenser : peut-on regrouper plusieurs types via une meta-data (un seul CPT Annonce avec un meta type qui prend les valeurs Offre, Demande, Echange) ?

Comment migrer un CPT vers un autre ?
Via WP-CLI : wp db query 'UPDATE wp_posts SET post_type = nouveau WHERE post_type = ancien', suivi de wp rewrite flush. Avec un export/import des donnees pour les cas complexes.

Les CPT impactent-ils la performance ?
Marginalement. Un CPT ajoute quelques lignes a la table wp_posts (memes lignes que les articles classiques). L impact se voit a partir de centaines de milliers d entrees, ou les requetes complexes (filtre par meta, jointures) deviennent lentes. Optimisations : indexes custom sur les meta_keys utilisees en requetes frequentes, et eventuellement table custom pour les CPT de gros volume.

References

Service ITSkillsCenter

Application mobile Android et iOS

Création d'application mobile Android et iOS. À partir de 350 000 FCFA.

Démarrer mon projet
Publicité