Intermediaire 1 min de lecture · 185 mots

Les Custom Post Types et Taxonomies : Guide complet

Introduction

Les Custom Post Types (CPT) et les taxonomies personnalisées constituent le fondement d’une architecture de contenu flexible et scalable dans WordPress. En 2025, avec l’évolution du Full Site Editing et l’importance croissante du SEO structuré, maîtriser ces outils est essentiel pour créer des sites WordPress performants et bien organisés. Ce guide vous accompagnera dans la création, l’optimisation et la gestion avancée des CPT et taxonomies.

Comprendre les Custom Post Types

Quand utiliser un Custom Post Type

Les Custom Post Types sont appropriés lorsque vous avez besoin de :

  • Séparer différents types de contenu (produits, témoignages, portfolios)
  • Appliquer des workflows de publication différents
  • Créer des templates spécifiques
  • Organiser du contenu avec des métadonnées distinctes
  • Améliorer le SEO avec des structures d’URL personnalisées
  • Exemples concrets :

  • Site immobilier : property (propriété)
  • Site événementiel : event (événement)
  • Site portfolio : project (projet)
  • Site e-learning : course (cours)
  • Créer un Custom Post Type complet

    Voici un exemple professionnel et testé pour un CPT « Portfolio » :

post_type . '*posts_columns', array( $this, 'custom_columns' ) );
        add_action( 'manage*' . $this->post_type . '*posts_custom_column', array( $this, 'custom_column_content' ), 10, 2 );
        add_filter( 'manage_edit-' . $this->post_type . '*sortable_columns', array( $this, 'sortable_columns' ) );
    }

    /**
     * Enregistrer le Custom Post Type
     */
    public function register_post_type() {
        $labels = array(
            'name'                  => *x( 'Projets Portfolio', 'Post type general name', 'mon-theme' ),
            'singular_name'         => *x( 'Projet', 'Post type singular name', 'mon-theme' ),
            'menu_name'             => *x( 'Portfolio', 'Admin Menu text', 'mon-theme' ),
            'name_admin_bar'        => *x( 'Projet', 'Add New on Toolbar', 'mon-theme' ),
            'add_new'               => **( 'Ajouter un projet', 'mon-theme' ),
            'add_new_item'          => **( 'Ajouter un nouveau projet', 'mon-theme' ),
            'new_item'              => **( 'Nouveau projet', 'mon-theme' ),
            'edit_item'             => **( 'Modifier le projet', 'mon-theme' ),
            'view_item'             => **( 'Voir le projet', 'mon-theme' ),
            'all_items'             => **( 'Tous les projets', 'mon-theme' ),
            'search_items'          => **( 'Rechercher des projets', 'mon-theme' ),
            'parent_item_colon'     => **( 'Projet parent:', 'mon-theme' ),
            'not_found'             => **( 'Aucun projet trouvé.', 'mon-theme' ),
            'not_found_in_trash'    => **( 'Aucun projet trouvé dans la corbeille.', 'mon-theme' ),
            'featured_image'        => *x( 'Image du projet', 'Overrides the "Featured Image" phrase', 'mon-theme' ),
            'set_featured_image'    => *x( 'Définir l'image du projet', 'Overrides the "Set featured image" phrase', 'mon-theme' ),
            'remove_featured_image' => *x( 'Retirer l'image du projet', 'Overrides the "Remove featured image" phrase', 'mon-theme' ),
            'use_featured_image'    => *x( 'Utiliser comme image du projet', 'Overrides the "Use as featured image" phrase', 'mon-theme' ),
            'archives'              => *x( 'Archives des projets', 'The post type archive label used in nav menus', 'mon-theme' ),
            'insert_into_item'      => *x( 'Insérer dans le projet', 'Overrides the "Insert into post" phrase', 'mon-theme' ),
            'uploaded_to_this_item' => *x( 'Téléversé vers ce projet', 'Overrides the "Uploaded to this post" phrase', 'mon-theme' ),
            'filter_items_list'     => *x( 'Filtrer la liste des projets', 'Screen reader text for the filter links', 'mon-theme' ),
            'items_list_navigation' => *x( 'Navigation de la liste des projets', 'Screen reader text for the pagination', 'mon-theme' ),
            'items_list'            => *x( 'Liste des projets', 'Screen reader text for the items list', 'mon-theme' ),
        );

        $args = array(
            'labels'             => $labels,
            'public'             => true,
            'publicly_queryable' => true,
            'show_ui'            => true,
            'show_in_menu'       => true,
            'query_var'          => true,
            'rewrite'            => array(
                'slug'       => 'portfolio',
                'with_front' => false,
                'feeds'      => true,
                'pages'      => true,
            ),
            'capability_type'    => 'post',
            'has_archive'        => true,
            'hierarchical'       => false,
            'menu_position'      => 5,
            'menu_icon'          => 'dashicons-portfolio',
            'supports'           => array(
                'title',
                'editor',
                'thumbnail',
                'excerpt',
                'custom-fields',
                'revisions',
                'author',
                'page-attributes',
            ),
            'show_in_rest'       => true,
            'rest_base'          => 'portfolio',
            'rest_controller_class' => 'WP_REST_Posts_Controller',
            'template'           => array(
                array( 'core/image', array() ),
                array( 'core/paragraph', array(
                    'placeholder' => **( 'Décrivez votre projet...', 'mon-theme' ),
                ) ),
            ),
            'template_lock'      => false,
        );

        register_post_type( $this->post_type, $args );
    }

    /**
     * Enregistrer les taxonomies
     */
    public function register_taxonomies() {
        // Taxonomie : Catégories de portfolio (hiérarchique)
        $category_labels = array(
            'name'              => *x( 'Catégories', 'taxonomy general name', 'mon-theme' ),
            'singular_name'     => *x( 'Catégorie', 'taxonomy singular name', 'mon-theme' ),
            'search_items'      => **( 'Rechercher des catégories', 'mon-theme' ),
            'all_items'         => **( 'Toutes les catégories', 'mon-theme' ),
            'parent_item'       => **( 'Catégorie parente', 'mon-theme' ),
            'parent_item_colon' => **( 'Catégorie parente:', 'mon-theme' ),
            'edit_item'         => **( 'Modifier la catégorie', 'mon-theme' ),
            'update_item'       => **( 'Mettre à jour la catégorie', 'mon-theme' ),
            'add_new_item'      => **( 'Ajouter une nouvelle catégorie', 'mon-theme' ),
            'new_item_name'     => **( 'Nom de la nouvelle catégorie', 'mon-theme' ),
            'menu_name'         => **( 'Catégories', 'mon-theme' ),
        );

        $category_args = array(
            'hierarchical'      => true,
            'labels'            => $category_labels,
            'show_ui'           => true,
            'show_admin_column' => true,
            'query_var'         => true,
            'rewrite'           => array(
                'slug'         => 'categorie-portfolio',
                'with_front'   => false,
                'hierarchical' => true,
            ),
            'show_in_rest'      => true,
            'rest_base'         => 'portfolio-categories',
        );

        register_taxonomy( 'portfolio_category', array( $this->post_type ), $category_args );

        // Taxonomie : Tags de portfolio (non hiérarchique)
        $tag_labels = array(
            'name'                       => *x( 'Tags', 'taxonomy general name', 'mon-theme' ),
            'singular_name'              => *x( 'Tag', 'taxonomy singular name', 'mon-theme' ),
            'search_items'               => **( 'Rechercher des tags', 'mon-theme' ),
            'popular_items'              => **( 'Tags populaires', 'mon-theme' ),
            'all_items'                  => **( 'Tous les tags', 'mon-theme' ),
            'edit_item'                  => **( 'Modifier le tag', 'mon-theme' ),
            'update_item'                => **( 'Mettre à jour le tag', 'mon-theme' ),
            'add_new_item'               => **( 'Ajouter un nouveau tag', 'mon-theme' ),
            'new_item_name'              => **( 'Nom du nouveau tag', 'mon-theme' ),
            'separate_items_with_commas' => **( 'Séparer les tags par des virgules', 'mon-theme' ),
            'add_or_remove_items'        => **( 'Ajouter ou retirer des tags', 'mon-theme' ),
            'choose_from_most_used'      => **( 'Choisir parmi les tags les plus utilisés', 'mon-theme' ),
            'menu_name'                  => **( 'Tags', 'mon-theme' ),
        );

        $tag_args = array(
            'hierarchical'      => false,
            'labels'            => $tag_labels,
            'show_ui'           => true,
            'show_admin_column' => true,
            'query_var'         => true,
            'rewrite'           => array(
                'slug' => 'tag-portfolio',
            ),
            'show_in_rest'      => true,
            'rest_base'         => 'portfolio-tags',
        );

        register_taxonomy( 'portfolio_tag', array( $this->post_type ), $tag_args );
    }

    /**
     * Changer le placeholder du titre
     */
    public function change_title_placeholder( $title, $post ) {
        if ( $post->post_type === $this->post_type ) {
            $title = **( 'Entrez le nom du projet', 'mon-theme' );
        }

        return $title;
    }

    /**
     * Ajouter des meta boxes
     */
    public function add_meta_boxes() {
        add_meta_box(
            'portfolio_details',
            **( 'Détails du projet', 'mon-theme' ),
            array( $this, 'render_details_meta_box' ),
            $this->post_type,
            'normal',
            'high'
        );

        add_meta_box(
            'portfolio_client',
            **( 'Informations client', 'mon-theme' ),
            array( $this, 'render_client_meta_box' ),
            $this->post_type,
            'side',
            'default'
        );
    }

    /**
     * Render meta box : Détails du projet
     */
    public function render_details_meta_box( $post ) {
        // Nonce pour la sécurité
        wp_nonce_field( 'portfolio_details_nonce', 'portfolio_details_nonce' );

        // Récupérer les valeurs existantes
        $project_url = get_post_meta( $post->ID, '*portfolio_project_url', true );
        $project_date = get_post_meta( $post->ID, '*portfolio_project_date', true );
        $technologies = get_post_meta( $post->ID, '*portfolio_technologies', true );
        $github_url = get_post_meta( $post->ID, '*portfolio_github_url', true );

        ?>
        

ID, '*portfolio_client_name', true ); $client_website = get_post_meta( $post->ID, '*portfolio_client_website', true ); $is_featured = get_post_meta( $post->ID, '*portfolio_is_featured', true ); ?>

post_type !== $this->post_type ) { return; } // Vérifier l'autosave if ( defined( 'DOING_AUTOSAVE' ) && DOING_AUTOSAVE ) { return; } // Vérifier les permissions if ( ! current_user_can( 'edit_post', $post_id ) ) { return; } // Sauvegarder les détails du projet if ( isset( $*POST['portfolio_details_nonce'] ) && wp_verify_nonce( $*POST['portfolio_details_nonce'], 'portfolio_details_nonce' ) ) { if ( isset( $*POST['portfolio_project_url'] ) ) { update_post_meta( $post_id, '*portfolio_project_url', esc_url_raw( $*POST['portfolio_project_url'] ) ); } if ( isset( $*POST['portfolio_project_date'] ) ) { update_post_meta( $post_id, '*portfolio_project_date', sanitize_text_field( $*POST['portfolio_project_date'] ) ); } if ( isset( $*POST['portfolio_technologies'] ) ) { update_post_meta( $post_id, '*portfolio_technologies', sanitize_text_field( $*POST['portfolio_technologies'] ) ); } if ( isset( $*POST['portfolio_github_url'] ) ) { update_post_meta( $post_id, '*portfolio_github_url', esc_url_raw( $*POST['portfolio_github_url'] ) ); } } // Sauvegarder les informations client if ( isset( $*POST['portfolio_client_nonce'] ) && wp_verify_nonce( $*POST['portfolio_client_nonce'], 'portfolio_client_nonce' ) ) { if ( isset( $*POST['portfolio_client_name'] ) ) { update_post_meta( $post_id, '*portfolio_client_name', sanitize_text_field( $*POST['portfolio_client_name'] ) ); } if ( isset( $*POST['portfolio_client_website'] ) ) { update_post_meta( $post_id, '*portfolio_client_website', esc_url_raw( $*POST['portfolio_client_website'] ) ); } $is_featured = isset( $*POST['portfolio_is_featured'] ) ? '1' : '0'; update_post_meta( $post_id, '*portfolio_is_featured', $is_featured ); } } /** * Colonnes personnalisées dans l'admin */ public function custom_columns( $columns ) { // Retirer la colonne date unset( $columns['date'] ); // Ajouter nos colonnes personnalisées $new_columns = array( 'thumbnail' => **( 'Image', 'mon-theme' ), 'client' => **( 'Client', 'mon-theme' ), 'technologies' => **( 'Technologies', 'mon-theme' ), 'project_date' => **( 'Date projet', 'mon-theme' ), 'is_featured' => **( 'Vedette', 'mon-theme' ), ); // Fusionner avec les colonnes existantes return array_merge( $columns, $new_columns ); } /** * Contenu des colonnes personnalisées */ public function custom_column_content( $column, $post_id ) { switch ( $column ) { case 'thumbnail': if ( has_post_thumbnail( $post_id ) ) { echo get_the_post_thumbnail( $post_id, array( 60, 60 ) ); } else { echo ''; } break; case 'client': $client_name = get_post_meta( $post_id, '*portfolio_client_name', true ); echo $client_name ? esc_html( $client_name ) : '-'; break; case 'technologies': $technologies = get_post_meta( $post_id, '*portfolio_technologies', true ); if ( $technologies ) { $tech_array = array_map( 'trim', explode( ',', $technologies ) ); echo '
'; foreach ( $tech_array as $tech ) { echo '' . esc_html( $tech ) . ' '; } echo '
'; } else { echo '-'; } break; case 'project_date': $project_date = get_post_meta( $post_id, '*portfolio_project_date', true ); if ( $project_date ) { echo esc_html( mysql2date( 'd/m/Y', $project_date ) ); } else { echo '-'; } break; case 'is_featured': $is_featured = get_post_meta( $post_id, '*portfolio_is_featured', true ); if ( $is_featured === '1' ) { echo ''; } else { echo ''; } break; } } /** * Colonnes triables */ public function sortable_columns( $columns ) { $columns['project_date'] = 'project_date'; $columns['client'] = 'client'; return $columns; } } // Initialiser le CPT new Portfolio();

Requêtes avancées avec WP_Query

Récupérer des projets portfolio avec filtres

 'portfolio',
    'posts_per_page' => 6,
    'meta_query'     => array(
        array(
            'key'     => '*portfolio_is_featured',
            'value'   => '1',
            'compare' => '=',
        ),
    ),
    'orderby'        => 'date',
    'order'          => 'DESC',
) );

if ( $featured_projects->have_posts() ) {
    while ( $featured_projects->have_posts() ) {
        $featured_projects->the_post();

        // Afficher le projet
        get_template_part( 'template-parts/content', 'portfolio' );
    }

    wp_reset_postdata();
}

// Récupérer les projets par catégorie et tag
$category_projects = new WP_Query( array(
    'post_type'      => 'portfolio',
    'posts_per_page' => 10,
    'tax_query'      => array(
        'relation' => 'AND',
        array(
            'taxonomy' => 'portfolio_category',
            'field'    => 'slug',
            'terms'    => 'web-design',
        ),
        array(
            'taxonomy' => 'portfolio_tag',
            'field'    => 'slug',
            'terms'    => array( 'wordpress', 'responsive' ),
            'operator' => 'IN',
        ),
    ),
) );

// Récupérer les projets d'un client spécifique
$client_projects = new WP_Query( array(
    'post_type'      => 'portfolio',
    'posts_per_page' => -1,
    'meta_query'     => array(
        array(
            'key'     => '*portfolio_client_name',
            'value'   => 'ACME Corporation',
            'compare' => '=',
        ),
    ),
) );

// Requête complexe : projets avec technologies et date
$complex_query = new WP_Query( array(
    'post_type'      => 'portfolio',
    'posts_per_page' => 12,
    'meta_query'     => array(
        'relation' => 'AND',
        array(
            'key'     => '*portfolio_technologies',
            'value'   => 'React',
            'compare' => 'LIKE',
        ),
        array(
            'key'     => '*portfolio_project_date',
            'value'   => '2024-01-01',
            'compare' => '>=',
            'type'    => 'DATE',
        ),
    ),
    'tax_query'      => array(
        array(
            'taxonomy' => 'portfolio_category',
            'field'    => 'slug',
            'terms'    => 'web-application',
        ),
    ),
    'orderby'        => array(
        'meta_value' => 'DESC',
        'date'       => 'DESC',
    ),
    'meta_key'       => '*portfolio_project_date',
) );

Optimisation des performances

Indexation de la base de données

Pour améliorer les performances des requêtes sur les meta données :

get_var(
        "SHOW INDEX FROM {$wpdb->postmeta}
        WHERE Key_name = 'meta_key_portfolio_featured'"
    );

    if ( ! $index_exists ) {
        $wpdb->query(
            "CREATE INDEX meta_key_portfolio_featured
            ON {$wpdb->postmeta} (meta_key(191), meta_value(50))
            WHERE meta_key = '*portfolio_is_featured'"
        );
    }

    // Index pour les dates de projet
    $date_index_exists = $wpdb->get_var(
        "SHOW INDEX FROM {$wpdb->postmeta}
        WHERE Key_name = 'meta_key_portfolio_date'"
    );

    if ( ! $date_index_exists ) {
        $wpdb->query(
            "CREATE INDEX meta_key_portfolio_date
            ON {$wpdb->postmeta} (meta_key(191), meta_value(20))
            WHERE meta_key = '*portfolio_project_date'"
        );
    }
}
add_action( 'after_switch_theme', 'montheme_add_portfolio_indexes' );

Mise en cache des requêtes

cache_group );

        if ( false === $projects ) {
            $query = new WP_Query( array(
                'post_type'      => 'portfolio',
                'posts_per_page' => $limit,
                'meta_query'     => array(
                    array(
                        'key'     => '*portfolio_is_featured',
                        'value'   => '1',
                        'compare' => '=',
                    ),
                ),
                'no_found_rows'  => true, // Optimisation : pas de pagination
                'update_post_meta_cache' => true,
                'update_post_term_cache' => true,
            ) );

            $projects = $query->posts;

            wp_cache_set( $cache_key, $projects, $this->cache_group, $this->cache_duration );
        }

        return $projects;
    }

    /**
     * Invalider le cache lors de la sauvegarde
     */
    public function invalidate_cache( $post_id, $post ) {
        if ( $post->post_type !== 'portfolio' ) {
            return;
        }

        // Supprimer tous les caches du groupe
        wp_cache_flush_group( $this->cache_group );
    }

    /**
     * Initialiser les hooks
     */
    public function init() {
        add_action( 'save_post_portfolio', array( $this, 'invalidate_cache' ), 10, 2 );
        add_action( 'delete_post', array( $this, 'invalidate_cache_on_delete' ) );
    }

    /**
     * Invalider le cache lors de la suppression
     */
    public function invalidate_cache_on_delete( $post_id ) {
        $post = get_post( $post_id );

        if ( $post && $post->post_type === 'portfolio' ) {
            wp_cache_flush_group( $this->cache_group );
        }
    }
}

$portfolio_cache = new PortfolioCacheManager();
$portfolio_cache->init();

Intégration avec Gutenberg

Créer un bloc personnalisé pour afficher les projets

 'montheme-portfolio-block',
        'editor_style'    => 'montheme-portfolio-block-editor',
        'style'           => 'montheme-portfolio-block',
        'render_callback' => 'montheme_render_portfolio_block',
        'attributes'      => array(
            'postsToShow' => array(
                'type'    => 'number',
                'default' => 6,
            ),
            'category' => array(
                'type'    => 'string',
                'default' => '',
            ),
            'featuredOnly' => array(
                'type'    => 'boolean',
                'default' => false,
            ),
            'columns' => array(
                'type'    => 'number',
                'default' => 3,
            ),
        ),
    ) );
}
add_action( 'init', 'montheme_register_portfolio_block' );

/**
 * Render du bloc portfolio
 */
function montheme_render_portfolio_block( $attributes ) {
    $posts_to_show = isset( $attributes['postsToShow'] ) ? absint( $attributes['postsToShow'] ) : 6;
    $category = isset( $attributes['category'] ) ? sanitize_text_field( $attributes['category'] ) : '';
    $featured_only = isset( $attributes['featuredOnly'] ) ? (bool) $attributes['featuredOnly'] : false;
    $columns = isset( $attributes['columns'] ) ? absint( $attributes['columns'] ) : 3;

    $args = array(
        'post_type'      => 'portfolio',
        'posts_per_page' => $posts_to_show,
        'no_found_rows'  => true,
    );

    if ( $category ) {
        $args['tax_query'] = array(
            array(
                'taxonomy' => 'portfolio_category',
                'field'    => 'slug',
                'terms'    => $category,
            ),
        );
    }

    if ( $featured_only ) {
        $args['meta_query'] = array(
            array(
                'key'     => '*portfolio_is_featured',
                'value'   => '1',
                'compare' => '=',
            ),
        );
    }

    $query = new WP_Query( $args );

    if ( ! $query->have_posts() ) {
        return '

' . esc_html**( 'Aucun projet trouvé.', 'mon-theme' ) . '

'; } ob_start(); ?>
have_posts() ) : $query->the_post(); ?>

SEO et structured data

Ajouter des données structurées Schema.org

 'https://schema.org',
        '@type'       => 'CreativeWork',
        'name'        => get_the_title(),
        'description' => get_the_excerpt(),
        'url'         => get_permalink(),
        'dateCreated' => get_the_date( 'c' ),
        'dateModified' => get_the_modified_date( 'c' ),
        'author'      => array(
            '@type' => 'Person',
            'name'  => get_the_author(),
        ),
    );

    // Ajouter l'image
    if ( has_post_thumbnail() ) {
        $image_id = get_post_thumbnail_id();
        $image_data = wp_get_attachment_image_src( $image_id, 'full' );

        if ( $image_data ) {
            $schema['image'] = array(
                '@type'  => 'ImageObject',
                'url'    => $image_data[0],
                'width'  => $image_data[1],
                'height' => $image_data[2],
            );
        }
    }

    // Ajouter les métadonnées personnalisées
    $project_url = get_post_meta( $post_id, '*portfolio_project_url', true );
    if ( $project_url ) {
        $schema['workExample'] = $project_url;
    }

    $project_date = get_post_meta( $post_id, '*portfolio_project_date', true );
    if ( $project_date ) {
        $schema['datePublished'] = mysql2date( 'c', $project_date );
    }

    $technologies = get_post_meta( $post_id, '*portfolio_technologies', true );
    if ( $technologies ) {
        $tech_array = array_map( 'trim', explode( ',', $technologies ) );
        $schema['keywords'] = implode( ', ', $tech_array );
    }

    $client_name = get_post_meta( $post_id, '*portfolio_client_name', true );
    if ( $client_name ) {
        $schema['client'] = array(
            '@type' => 'Organization',
            'name'  => $client_name,
        );

        $client_website = get_post_meta( $post_id, '*portfolio_client_website', true );
        if ( $client_website ) {
            $schema['client']['url'] = $client_website;
        }
    }

    // Sortir le JSON-LD
    echo '' . "n";
}
add_action( 'wp_head', 'montheme_portfolio_structured_data' );

REST API pour CPT

Ajouter des champs personnalisés à l'API REST

 function( $post ) {
            return get_post_meta( $post['id'], '*portfolio_is_featured', true ) === '1';
        },
        'update_callback' => function( $value, $post ) {
            return update_post_meta( $post->ID, '*portfolio_is_featured', $value ? '1' : '0' );
        },
        'schema'          => array(
            'description' => **( 'Si le projet est vedette', 'mon-theme' ),
            'type'        => 'boolean',
        ),
    ) );

    // Enregistrer tous les champs méta en une seule fois
    register_rest_field( 'portfolio', 'project_meta', array(
        'get_callback' => function( $post ) {
            return array(
                'project_url'    => get_post_meta( $post['id'], '*portfolio_project_url', true ),
                'project_date'   => get_post_meta( $post['id'], '*portfolio_project_date', true ),
                'technologies'   => get_post_meta( $post['id'], '*portfolio_technologies', true ),
                'github_url'     => get_post_meta( $post['id'], '*portfolio_github_url', true ),
                'client_name'    => get_post_meta( $post['id'], '*portfolio_client_name', true ),
                'client_website' => get_post_meta( $post['id'], '*portfolio_client_website', true ),
            );
        },
        'schema' => array(
            'description' => **( 'Métadonnées du projet', 'mon-theme' ),
            'type'        => 'object',
            'properties'  => array(
                'project_url'    => array( 'type' => 'string', 'format' => 'uri' ),
                'project_date'   => array( 'type' => 'string', 'format' => 'date' ),
                'technologies'   => array( 'type' => 'string' ),
                'github_url'     => array( 'type' => 'string', 'format' => 'uri' ),
                'client_name'    => array( 'type' => 'string' ),
                'client_website' => array( 'type' => 'string', 'format' => 'uri' ),
            ),
        ),
    ) );
}
add_action( 'rest_api_init', 'montheme_register_portfolio_rest_fields' );

Erreurs courantes à éviter

1. Oublier de flush les rewrite rules

PROBLÈME : URLs 404 après création du CPT

SOLUTION :

// Lors de l'activation du thème ou plugin
function montheme_flush_rewrite_rules() {
    // Enregistrer le CPT
    montheme_register_portfolio_cpt();

    // Flush
    flush_rewrite_rules();
}
register_activation_hook( **FILE__, 'montheme_flush_rewrite_rules' );

2. Utiliser un slug trop générique

MAUVAIS :

'rewrite' => array( 'slug' => 'item' )

BON :

'rewrite' => array( 'slug' => 'portfolio-projet' )

3. Ne pas penser au SEO

MAUVAIS : Pas de has_archive, pas de métadonnées

BON : Archive activée, structured data, sitemap XML

Conclusion

La maîtrise des Custom Post Types et des taxonomies est essentielle pour créer des sites WordPress structurés et performants en 2025. En combinant une architecture solide, des optimisations de performance et une intégration SEO appropriée, vous créerez des solutions évolutives et maintenables.

Points clés :

  • Planifiez votre architecture de contenu avant de coder
  • Utilisez des slugs descriptifs et uniques
  • Implémentez la mise en cache pour les performances
  • Ajoutez des données structurées pour le SEO
  • Exposez vos CPT via l'API REST pour les applications découplées
  • Flush les rewrite rules uniquement à l'activation
  • Ressources supplémentaires

  • Post Types Handbook
  • Taxonomies Handbook
  • WP_Query Reference

  • Mots-clés: Custom Post Types WordPress, CPT WordPress, taxonomies WordPress, WP_Query avancé, post types personnalisés, WordPress metadata, structured data WordPress, WordPress SEO, portfolio WordPress, CPT performance

    Une remarque, un retour ?

    Cet article est vivant - corrections, contre-arguments et retours de production sont les bienvenus. Trois canaux, choisissez celui qui vous convient.

    Laisser un commentaire