
Créer des custom post types dans WordPress sans plugin
Selon votre cahier des charges ou la complexité de votre site internet, vous pouvez être amené à créer des custom post types dans WordPress.
Dans ce tutoriel, je vais vous guider pour les créer sans plugin !
Qu’est-ce qu’un custom post type ?
Les custom post types constituent l’une des fonctionnalités les plus puissantes et polyvalentes de WordPress. De base, lorsque vous l’installez, vous avez 2 types de contenus : les articles (posts) et les pages. C’est ce que l’on appelle des post types.
Le CMS vous propose de créer vous-même vos propres post types pour du contenu personnalisé. Ainsi, nul besoin de chercher vos services ou produits au milieu de vos pages dans l’administration. Il vous suffira de vous rendre dans l’onglet correspondant dans l’administration.
Selon vos besoins, vous pourrez créer des contenus personnalisés pour votre site internet : une section “Services”, une autre “Portfolio”, une troisième “Soins”, etc. Tout dépendra de votre cahier des charges.
Un avantage certain est que vous pourrez attribuer un template (modèle de page) à chaque custom post type, et ainsi afficher un certain nombre de ces articles dans vos pages. Les plugins que vous installez ont leurs propres custom post types : WooCommerce propose des produits, les plugins de réservation des événements, les plugins de restaurant des menus ou des ingrédients, etc.
Vous pourrez également leur attacher des taxonomies, c’est-à-dire des catégories ou des étiquettes, et décider s’ils possèdent ou non leur propre URL. Nous examinerons tous ces points un peu plus en détail.
Créer ses custom post types dans WordPress sans plugin
La création
Pour un niveau intermédiaire, nous allons nous charger de créer nos propres post types dans un fichier de notre thème à partir de zéro ou dans un thème enfant.
Pour commencer, je vais créer un dossier “assets” que je placerai dans mon thème.

On va y placer notre fichier custom-post-types.php

Tout d’abord, créons une fonction nommée “inumedia_mon_custom_post_type()“, à l’intérieur de laquelle nous allons définir une variable “$labels” qui nous permettra de modifier les textes de nos boutons, menus, etc.
<?php
// Déclaration des Custom Post types
// Cette fonction crée un type de publication personnalisé appelé 'Portfolio' pour les projets.
function inumedia_mon_custom_post_type() {
// Définition des étiquettes pour le type de publication personnalisé.
$labels = array(
'name' => __( 'Portfolio' ), // Nom de mon custom post type
'singular_name' => __( 'Portfolio' ), // Nom au singulier
'menu_name' => __( 'Portfolio' ), // Nom apparaissant dans le menu
'all_items' => __( 'Tous les projets' ), // Titre pour "Tous les projets
'view_item' => __( 'Voir le projet' ), // Titre du lien pour afficher la page en front-end
'add_new_item' => __( 'Ajouter un projet' ), // Texte du bouton "Ajouter un article" pour créer un nouveau post
'add_new' => __( 'Ajouter' ), // Texte du lien "Ajouter un article" dans le menu
'search_items' => __( 'Rechercher un projet' ), // Texte du bouton "rechercher" Dans la liste de vos posts
'not_found' => __( 'Projet non trouvé' ), // Texte du résultat "Non trouvé" quand une recherche des posts du cpt est faite
'not_found_in_trash' => __( 'Non trouvé dans la corbeille' ), // Texte du résultat "Non trouvé dans la corbeille" quand une recherche des posts du cpt est faite dans la corbeille
);
}
// Déclenche la fonction de l'initialisation de WordPress.
add_action( 'init', 'inumedia_mon_custom_post_type', 0 );
Explications :





Après la fin du $labels, ajoutons à présent les arguments :
<?php
$args = array(
'label' => __( 'portfolio' ), // L'identifiant de notre custom post type
'description' => __( 'Mes projets' ), // On lui donne une desription
'labels' => $labels, // On appelle nos labels définis précédement
'menu_icon' => 'dashicons-archive', // Icône pour le menu d'administration.
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail'), // Types de supports autorisés.
'public' => true, // Indique si le type de publication doit être publique.
'hierarchical' => false, // Indique si les publications peuvent avoir des enfants (comme les pages).
'rewrite' => array( 'slug' => 'portfolio', 'with_front' => false ), // Réécriture d'URL pour les publications, ici ça s'affichera comme www.monsite.fr/portfolio/mon-titre-projet.
'show_in_rest' => true, // Indique si l'éditeur de blocs doit être activé.
'show_ui' => true, // Indique si l'interface utilisateur doit être affichée.
'show_in_menu' => true, // Indique si le type de publication doit apparaître dans le menu d'administration.
'show_in_nav_menus' => true, // Indique si le type de publication doit apparaître dans les menus de navigation.
'show_in_admin_bar' => true, // Indique si le type de publication doit apparaître dans la barre d'administration.
'has_archive' => false, // Indique si le type de publication doit avoir une archive.
'can_export' => true, // Indique si le type de publication peut être exporté.
'query_var' => true, // Indique si le type de publication a une variable de requête.
'exclude_from_search' => false, // Indique si le type de publication doit être exclu des résultats de recherche.
'publicly_queryable' => true, // Indique si le type de publication peut être utilisé dans des loops.
'capability_type' => 'post', // Type de capacité pour le type de publication.
'menu_position' => 5 // Position du menu dans le tableau de bord WordPress.
);
// Enregistre le type de publication personnalisé avec les paramètres définis ci-dessus.
register_post_type( 'portfolio', $args );
}
// Déclenche la fonction de l'initialisation de WordPress.
add_action( 'init', 'inumedia_mon_custom_post_type', 0 );
Explications :
- menu_icon : Cette icône peut être modifiée en utilisant la bibliothèque d’icônes de WordPress appelée “Dashicons“, ou en utilisant une icône personnalisée.
Pour utiliser les icônes de WordPress, rendez-vous sur ce site, sélectionnez l’icône désirée, copiez l’identifiant et utilisez-le.

- supports : Il s’agit de tous les éléments dont vous pouvez avoir besoin dans votre article :
- Le titre (title) : Le champ titre du custom post type. Sans titre, l’ID est utilisé par défaut.
- L’extrait (extract) : Il s’agit de quelques lignes qui résument l’article et sont généralement affichées dans les vignettes de blog.
- L’image de couverture (thumbnail) : L’image qui illustre votre article.
- Le contenu (editor) : Si Gutenberg est activé (voir plus bas), il s’agira des blocs de Gutenberg, sinon de l’ancienne interface avec un éditeur WYSIWYG.
- L’auteur (author) : L’auteur de l’article.
- Les pings (trackbacks) : L’autorisation d’activer ou non les liens pointant vers votre article (attention au spam).
- Les commentaires (comments) : L’autorisation d’activer ou non les commentaires (attention au spam).
- Les champs personnalisés (custom-fields) : Les métadonnées qui peuvent être rattachées à vos articles dans certains thèmes.
- Les révisions (revisions) : Toutes les révisions de vos articles, ce qui vous permet de revenir en arrière lors de manipulations.
- Les attributs de pages (modèles de pages et ordre ) : Notez que “hierarchical” doit être défini sur “true” pour que cela fonctionne. Comme pour les pages, cela affichera la possibilité de choisir des modèles (templates). Si “hierarchical” est défini sur false, cela peut se faire automatiquement en créant un fichier single-nom_cpt.php dans votre thème que vous configurerez en HTML/PHP.
- Les formats d’articles (post-formats) : Choix pour un format galerie, audio, etc.
- capability_type : Il s’agit du rôle utilisateur qui peut voir le custom post type. Ici tous els utilisateurs ayant accès aux articles, ont la possibilité de le voir et de l’éditer. Pour modifier cet élément, il faudra juste remplacer par la capacité voulue (manage_categories…), reportez vous à mon article sur les rôles utilisateurs de WordPress.
- menu_position : Il s’agit de la position dans le menu latéral de WordPress. Iici la position 5 est simimilaire à celle des articles. Mon item se place donc vers les articles de Word¨ress. Voici les positions par défaut des menus existants :
- 2 – Tableau de bord
- 4 – Séparateur
- 5 – Articles
- 10 – Média
- 20 – Pages
- 25 – Commentaires
- 29 – Séparateur
- 60 – Apparance
- 65 – Plugins
- 70 – Utilisateurs
- 75 – Outils
- 80 – Réglages
- 99 – Séparateur
Nous avons donc notre code complet sans catégories et étiquettes :
<?php
// Déclaration des Custom Post types
// Cette fonction crée un type de publication personnalisé appelé 'Portfolio' pour les tutoriels WordPress.
function inumedia_mon_custom_post_type() {
// Définition des étiquettes pour le type de publication personnalisé.
$labels = array(
'name' => __( 'Portfolio' ), // Nom de mon custom post type
'singular_name' => __( 'Portfolio' ), // Nom au singulier
'menu_name' => __( 'Portfolio' ), // Nom apparaissant dans le menu
'all_items' => __( 'Tous les projets' ), // Titre pour "Tous les projets
'view_item' => __( 'Voir le projet' ), // Titre du lien pour afficher la page en front-end
'add_new_item' => __( 'Ajouter un projet' ), // Texte du bouton "Ajouter un article" pour créer un nouveau post
'add_new' => __( 'Ajouter' ), // Texte du lien "Ajouter un article" dans le menu
'edit_item' => __( 'Modifier le projet' ), // Texte du bouton "Modifier l'article" sur le post
'update_item' => __( 'Mettre à jour le projet' ), // Texte du bouton "Mettre à jour" du post
'search_items' => __( 'Rechercher un projet' ), // Texte du bouton "rechercher" Dans la liste de vos posts
'not_found' => __( 'Projet non trouvé' ), // Texte du bouton "Non trouvé" quand une recherche des posts du cpt est faite
'not_found_in_trash' => __( 'Projet non trouvé dans la corbeille' ), // Texte du bouton "Non trouvé dans la corbeille" quand une recherche des posts du cpt est faite
);
// Paramètres pour le type de publication personnalisé.
$args = array(
'label' => __( 'portfolio' ),
'description' => __( 'Mes projets' ),
'labels' => $labels,
'menu_icon' => 'dashicons-archive', // Icône pour le menu d'administration.
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail'), // Types de supports autorisés.
'public' => true, // Indique si le type de publication doit être public.
'hierarchical' => false, // Indique si les publications peuvent avoir des enfants.
'rewrite' => array( 'slug' => 'portfolio', 'with_front' => false ), // Réécriture d'URL pour les publications.
'show_in_rest' => true, // Indique si l'éditeur de blocs doit être activé.
'show_ui' => true, // Indique si l'interface utilisateur doit être affichée.
'show_in_menu' => true, // Indique si le type de publication doit apparaître dans le menu d'administration.
'show_in_nav_menus' => true, // Indique si le type de publication doit apparaître dans les menus de navigation.
'show_in_admin_bar' => true, // Indique si le type de publication doit apparaître dans la barre d'administration.
'has_archive' => false, // Indique si le type de publication doit avoir une archive.
'can_export' => true, // Indique si le type de publication peut être exporté.
'query_var' => true, // Indique si le type de publication a une variable de requête.
'exclude_from_search' => false, // Indique si le type de publication doit être exclu des résultats de recherche.
'publicly_queryable' => true, // Indique si le type de publication peut être interrogé publiquement.
'capability_type' => 'post', // Type de capacité pour le type de publication.
'menu_position' => 5 // Position du menu dans le tableau de bord WordPress.
);
// Enregistre le type de publication personnalisé avec les paramètres définis ci-dessus.
register_post_type( 'portfolio', $args );
}
// Déclenche la fonction inumedia_mon_custom_post_type lors de l'initialisation de WordPress.
add_action( 'init', 'inumedia_mon_custom_post_type', 0 );
Nous avons donc notre code complet sans catégories et étiquettes. Pour qu’il soit fonctionnel, il faudra appeler custom-post-types.php dans le fichier functions.php de notre thème.
<?php
// Chargement des custom post types
require_once('assets/custom-post-types.php');
?>
Vous devriez à présent apercevoir votre custom post type “Portfolio”.
Attacher une ou plusieurs taxonomies à mon custom post type
Pour attacher une ou plusieurs taxonomies à mon custom post type, les taxonomies étant les “catégories” ou étiquettes (tags), elles vont vous permettre d’organiser toute votre information présente dans ce contenu personnalisé et de faire des filtres, comme vous le feriez avec les articles (posts).
Pour les attacher, nous ajouterons ce code dans custom-post-types.php après “action” :
<?php
function inumedia_mon_custom_post_type_taxonomies() {
// Taxonomie "typeprojet" // similaire à une catégorie
$labels_typeprojet = array(
'name' => _x( 'Type de projet', 'Nom général' ), // Nom de la catégorie
'singular_name' => _x( 'Catégorie de projet', 'Nom au singulier' ),// Nom de la catégorie au singulier
'search_items' => __( 'Rechercher une catégorie' ), // Bouton rechercher parmis les catégorie
'all_items' => __( 'Toutes les catégories' ), // Nom de la catégorie au singulier
'parent_item' => __( 'Catégorie parente' ), // Intitulé pour la catégorie parente
'parent_item_colon' => __( 'Catégorie parente :' ),
'edit_item' => __( 'Modifier la catégorie' ), // Titre de la page de modification de la catégorie
'update_item' => __( 'Mettre à jour la catégorie' ), // Bouton mettre à jour la catégorie
'add_new_item' => __( 'Ajouter une catégorie de projet' ), // Bouton et titre de page Ajouter une catégorie
'new_item_name' => __( 'Nouvelle catégorie de projet' ),
'menu_name' => __( 'Catégories portfolio' ), // intitulé du menu
);
register_taxonomy( 'typeprojet', array( 'portfolio' ), array( // on rattache le slug de notre taxonomie "typeprojet" à notre cpt
"labels" => $labels_typeprojet, // on attribue les labels précédement cités
"hierarchical" => true, // C'est hierarchique (on peut donc avoir des sous catégories)
"public" => true, // On la rend publique, si on met false, on ne pourra pas y accéder via des liens directs ou des URL.
"show_ui" => true, // Afficher l'interface utilisateur pour la gestion de cette taxonomie dans le back-end
"show_admin_column" => true, // Afficher une colonne d'administration dans la liste des éléments de la taxonomie
"show_in_navmenus" => true, // Afficher cette taxonomie dans les menus de navigation
"show_tagcloud" => true, // Afficher cette taxonomie dans le nuage de tags
"show_in_rest" => true // Autoriser l'utilisation de cette taxonomie dans l'API REST
));
// Taxonomie "villes" // similaire aux tags / étiquettes
$labels_villes = array(
'name' => _x( 'Villes', 'Nom général' ),
'singular_name' => _x( 'Ville', 'Nom au singulier' ),
'search_items' => __( 'Rechercher une ville' ),
'all_items' => __( 'Tous les villes' ),
'edit_item' => __( 'Modifier la ville' ),
'update_item' => __( 'Mettre à jour la ville' ),
'add_new_item' => __( 'Ajouter une ville' ),
'new_item_name' => __( 'Nouvelle ville' ),
'menu_name' => __( 'Villes' ),
'not_found' => __( 'Aucune ville trouvée' ),
'no_terms' => __( 'Aucune ville' ),
'items_list_navigation' => __( 'Navigation de la liste des villes' ),
'items_list' => __( 'Liste des villes' ),
);
register_taxonomy( 'villes', array( 'portfolio' ), array(
"labels" => $labels_villes,
"hierarchical" => false, // Ce n'est pas hierarchique, c'est donc un équivalent des tags / étiquettes
"public" => true,
"show_ui" => true,
"show_admin_column" => true,
"show_in_nav_menus" => false,
"show_tagcloud" => false,
"show_in_rest" => true,
"capabilities" => array( // on donne quelques droits spécifiques
'manage_terms' => 'manage_categories', // Gérer les termes de la taxonomie comme les catégories accessible uniquement à ceux pouvant gérer les catégories (admin)
'edit_terms' => 'manage_categories', // Modifier les termes de la taxonomie comme les catégories accessible uniquement à ceux pouvant gérer les catégories (admin)
'delete_terms' => 'manage_categories', // Supprimer les termes de la taxonomie comme les catégories accessible uniquement à ceux pouvant gérer les catégories (admin)
'assign_terms' => 'edit_posts' // Assigner des termes à des contenus accessible à ceux pouvant modifier les articles
),
));
}
add_action( 'init', 'inumedia_mon_custom_post_type_taxonomies', 0 );
?>
On peut également attribuer la capacité de gestion, la modification, la suppression et l’assignation à un même rôle utilisateur.
Mais dans des projets spécifiques, par exemple de la rédaction d’articles, il peut être intéressant de ne donner qu’un droit d’utilisation de taxonomies aux éditeurs.
Voilà, vous avez créé vos taxonomies.
Nous verrons un peu plus bas comment faire figurer votre taxonomie dans l’url de votre article.
Quelques spécificités
Ne pas faire apparaitre les urls (permaliens) des contenus personnalisés
Je les utilise pour créer une documentation à télécharger pour certains clients. Dans ce cas, nous n’avons pas besoin d’accéder à l’article, mais uniquement au fichier PDF qui sera à l’intérieur. Ainsi, on va donc désactiver le permalien. On pourra aller chercher notre document PDF grâce à une requête PHP sous forme de boucle et l’afficher dans la page souhaitée.
Il vous faudra faire les modifications suivantes dans votre fonction inumedia_mon_custom_post_type()
'public' => false, // S'il n'est pas public, il n'aura pas d'url
'publicly_queryable' => true, // On le garde tout de même accessible avec des loops
'show_ui' => true, // On laisse la possibilité de le modifier via wp-admin
'exclude_from_search' => true, // On ne l'affiche pas dans les résultats de recherche
'has_archive' => false, // Il n'a pas besoin de page d'archives / Catégories
'rewrite' => false, // On enleve les conditions de rewrite
Créer des custom post types, mais supprimer Gutenberg dans les articles du CPT
Dans le cas de ma documentation, Gutenberg ne m’est d’aucune utilité. J’aurai juste besoin du titre et d’un champ “fichier” que je vais créer avec ACF.
Toujours dans ma fonction inumedia_mon_custom_post_type() :
'supports' => array( 'title'), // Types de supports autorisés.
'show_in_rest' => false, // Indique si l'éditeur de blocs doit être activé.
Ainsi, votre custom post type ne sera composé que du titre. Si vous gardez l’éditeur, vous aurez accès à l’ancien éditeur de texte de WordPress.
Réécriture des URLS pour les taxonomies
Le problème, c’est que l’accès à la liste de vos posts ou vos URL peut poser problème. Même si mes permaliens sont configurés sur / %category% / %postname% /
, l’URL de mon article reste en monsite.fr/mon-cpt/mon-article
. Il faut donc créer une règle d’écriture pour notre contenu personnalisé en y intégrant la taxonomie.
// Dans votre fonction inumedia_mon_custom_post_type() il faut changer la ligne du rewrite :
'rewrite' => array( 'slug' => 'portfolio/%typeprojet%', 'with_front' => false ), // Réécriture d'URL pour les publications.
// ici j'ai mis portfolio, j'aurais pu mettre nos-projets ou un autre terme. Il s'agira juste de l'url en front-end. Le backend restera sur le slug de notre cpt
Ensuite, on va créer une fonction qui va remplacer %typeprojet% par l’intitulé de notre taxonomie (par exemple : paysagiste). On la placera après la fonction des taxonomies.
function inumedia_rewrite_url_taxonomie_projet( $post_link, $post = 0, $leavename = false ) {
if ( $post->post_type === 'portfolio' ) {
$terms_categorie = wp_get_object_terms( $post->ID, 'typeprojet' );
if ( ! empty( $terms_categorie ) && ! is_wp_error( $terms_categorie ) ) {
$post_link = str_replace( '%typeprojet%', $terms_categorie[0]->slug, $post_link );
}
}
return $post_link;
}
add_filter( 'post_type_link', 'inumedia_rewrite_url_taxonomie_projet', 10, 3 );
Conclusion : créer des custom post types, c’est incontournable !
Vous avez compris que les custom post type dans WordPress sont extrêmement utiles pour organiser votre contenu.
Chaque projet WordPress que j’ai réalisé intègre des custom post types, ne serait-ce que pour référencer des pages en lien avec votre activité.
Maîtriser leur logique et leur fonctionnement vous permettra de créer un contenu pertinent et bien organisé pour votre site internet. De plus, établir une hiérarchie claire ne pourra qu’améliorer votre référencement.
Bénéficiez de 30 minutes gratuites pour me parler de votre problème technique