Paul Schuhmacher
Durée : 28h
Novembre 2025
Objectifs : Créer ses propres types de données et développer ses interfaces pour les administrer
Rappelons-nous le schéma de la base de données WordPress
posts),
pages (page), Custom Post Type (CPT);Là , WordPress devient un CMS (encore plus) intéressant !
Créer un CPT avec la fonction register_post_type()
:
function bp_register_cpt_book() {
//Labels (affichage dans les écrans d'admin)
$label = array(
...
);
//Options du CPT
$args = array(
'labels' => $label
...
);
// Enregistrement du CPT
register_post_type('book', $args);
}Enregistrer la fonction créant le CPT sur le
hook init :
add_action('init', 'bp_register_cpt_book', 0);Pensez à TOUJOURS préfixer vos fonctions par votre nom vendor !
Architecture : Lâenregistrement de CPT est critique au fonctionnement du site. Ă placer dans un plugin ou mu-plugin.
Nâutilisez pas un des noms suivants !
post
page
attachment
revision
nav_menu_item
custom_css
customize_changeset
oembed_cache
user_request
wp_block
wp_global_styles
wp_navigation
wp_template
wp_template_partCopier/Coller dans le projet (dans
functions.php pour le moment) et faisons le tour des
options disponibles.
/**
* Description: Crée un Custom Post Type "Book" pour la bibliothÚque.
* Version: 1.0
*/
/**
* Fonction pour enregistrer le Custom Post Type "Book"
*/
function bp_register_cpt_book() {
// Labels affichés dans l'admin (Les générer (Plugin, IA, Site web!).
$labels = array(
'name' => _x( 'Livres', 'Post Type General Name', 'textdomain' ),
'singular_name' => _x( 'Livre', 'Post Type Singular Name', 'textdomain' ),
'menu_name' => __( 'Livres', 'textdomain' ),
'name_admin_bar' => __( 'Livre', 'textdomain' ),
'archives' => __( 'Archives des livres', 'textdomain' ),
'attributes' => __( 'Attributs du livre', 'textdomain' ),
'parent_item_colon' => __( 'Livre parent :', 'textdomain' ),
'all_items' => __( 'Tous les livres', 'textdomain' ),
'add_new_item' => __( 'Ajouter un nouveau livre', 'textdomain' ),
'add_new' => __( 'Ajouter', 'textdomain' ),
'new_item' => __( 'Nouveau livre', 'textdomain' ),
'edit_item' => __( 'Ăditer le livre', 'textdomain' ),
'update_item' => __( 'Mettre Ă jour le livre', 'textdomain' ),
'view_item' => __( 'Voir le livre', 'textdomain' ),
'view_items' => __( 'Voir les livres', 'textdomain' ),
'search_items' => __( 'Rechercher un livre', 'textdomain' ),
'not_found' => __( 'Aucun livre trouvé', 'textdomain' ),
'not_found_in_trash' => __( 'Aucun livre trouvé dans la corbeille', 'textdomain' ),
'featured_image' => __( 'Image Ă la une', 'textdomain' ),
'set_featured_image' => __( 'DĂ©finir lâimage Ă la une', 'textdomain' ),
'remove_featured_image' => __( 'Supprimer lâimage Ă la une', 'textdomain' ),
'use_featured_image' => __( 'Utiliser comme image Ă la une', 'textdomain' ),
'insert_into_item' => __( 'Insérer dans le livre', 'textdomain' ),
'uploaded_to_this_item' => __( 'Téléversé sur ce livre', 'textdomain' ),
'items_list' => __( 'Liste de livres', 'textdomain' ),
'items_list_navigation' => __( 'Navigation liste de livres', 'textdomain' ),
'filter_items_list' => __( 'Filtrer la liste de livres', 'textdomain' ),
);
// Options principales du CPT.
$args = array(
'label' => __( 'Livre', 'textdomain' ),
'description' => __( 'Custom Post Type pour les livres', 'textdomain' ),
'labels' => $labels,
'supports' => array( 'title', 'editor', 'thumbnail', 'excerpt' ), // Important : pour les options d'édition dans la page admin.
'taxonomies' => array(), // Important : Enregistrer les taxonomies Ă utiliser par le CPT.
'hierarchical' => false, // true pour type parent/enfant (comme pages).
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'menu_position' => 4,
'menu_icon' => 'dashicons-book', // icĂŽne dans l'interface (important pour l'UI).
'show_in_admin_bar' => true,
'show_in_nav_menus' => true,
'register_meta_box_cb' => '', // Fonction Ă appeler pour enregistrer auto des MetaBox.
'can_export' => true,
'has_archive' => true, // Important ! Permet d'avoir template archive custom (archive-book.php).
'exclude_from_search' => false,
'publicly_queryable' => true,
'capability_type' => 'post', // Important pour définir les capabilities (attachées aux roles !). Meme que sur posts par défaut.
'show_in_rest' => true, // Pour Gutenberg et autres Page builder (API utilisĂ©e par les blocks) et lâAPI REST.
'rewrite' => array(
'slug' => 'books', // ! Important ! Permet de réécrire le slug dans les urls. Mettre au pluriel.
),
'delete_with_user' => false,
);
// Enregistrement du CPT.
register_post_type( 'livre', $args );
}
// Hook pour initialiser le CPT aprĂšs le chargement du core.
add_action( 'init', 'bp_register_cpt_book', 0 );WordPress fournit plusieurs fonctions dâinternationalisation pour rendre les chaĂźnes traduisibles dont les fonctions :
__( string $text, string $domain = 'default' ): string
: Retourne une chaĂźne traduite sans contexte._x( string $text, string $context, string $domain = 'default' ): string
: Retourne une chaĂźne traduite avec un
contexteLe contexte aide les traducteur·ices Ă choisir la bonne traduction, quand un mĂȘme mot peut avoir plusieurs sens selon⊠le contexte.
// Le contexte est Post Type Singular Name, le domaine de mon theme/plugin est 'bp_domain'
_x('Livre', 'Post Type Singular Name', 'bp_domain');En savoir plus sur lâinternationalisation et la localisation dans WordPress
dashicons-admin-post.Le projet va élargir sa collection avec 36 nouvelles icÎnes prochainement.
Utiliser rewrite:
slug : (conseillé) Réécrire le
slug du CPT, notamment le mettre au pluriel pour
homogĂ©nĂ©iser les noms dâURL (/books est une collection) ou
le changer pour Ă©viter ambiguĂŻtĂ©s/collisions avec dâautres ressources du
site. Permet de découpler le nom du CPT et son
URL.'rewrite' => array(
'slug' => 'books',
)Remarque : Utiliser lâoption
rewritene change pas le nom du posttype (pour les templates et la template hierarchy! Ici, leposttypereste bienbook).
On réécrire les capabilities plus tard pour implémenter nos rÚgles de gestion et notre processus éditorial.
Dans la mĂȘme veine, crĂ©er une CT avec register_taxonomy().
/**
* Déclare la taxonomie "Genre" pour le post type "book".
*/
function bp_register_taxonomy_genre() {
//Labels
$labels = array(
...
);
//Options
$args = array(
...
),
);
//Enregistrer la CT
register_taxonomy(
'genre', array('book'), $args
);
}
//Ajouter l'enregistrement de la CT sur le hook init (comme CPT).
add_action( 'init', 'bp_register_taxonomy_genre' );Copier/coller dans le theme (toujours dans functions.php
pour lâinstant) et faisons le tour.
/**
* Déclare la taxonomie "Genre" pour le post type "book".
*/
function bp_register_taxonomy_genre() {
$labels = array(
'name' => _x( 'Genres', 'taxonomy general name', 'textdomain' ),
'singular_name' => _x( 'Genre', 'taxonomy singular name', 'textdomain' ),
'search_items' => __( 'Rechercher un genre', 'textdomain' ),
'all_items' => __( 'Tous les genres', 'textdomain' ),
'parent_item' => __( 'Genre parent', 'textdomain' ),
'parent_item_colon' => __( 'Genre parent :', 'textdomain' ),
'edit_item' => __( 'Modifier le genre', 'textdomain' ),
'update_item' => __( 'Mettre Ă jour le genre', 'textdomain' ),
'add_new_item' => __( 'Ajouter un nouveau genre', 'textdomain' ),
'new_item_name' => __( 'Nouveau nom de genre', 'textdomain' ),
'menu_name' => __( 'Genres', 'textdomain' ),
);
$args = array(
'labels' => $labels,
// Taxonomie hiérarchique (true = comme catégories ; false = comme étiquettes).
'hierarchical' => true,
// Permet de gĂ©rer la taxonomie dans lâadmin.
'show_ui' => true,
'show_admin_column' => true,
// Disponible dans les requĂȘtes publiques (WP_Query).
'public' => true,
// Réécriture de lâURL.
'rewrite' => array(
'slug' => 'genre', // URL de base : /genre/...
'hierarchical' => true, // permet /genre/roman/policier/ .
),
// Personnaliser la MetaBox. Ici utilise la mĂȘme que post_categories_meta_box (avoir meme UI que si hiĂ©rarchique, avec checkboxes).
'meta_box_cb' => 'post_categories_meta_box',
);
register_taxonomy(
'genre', // slug de la taxonomie.
array( 'book' ), // CPT sur lequel elle s'applique.
$args
);
}
add_action( 'init', 'bp_register_taxonomy_genre' );genre pour catégoriser les livres
popularity pour catégoriser les livres
classic,
best-seller/genre/romans,
/genre/essais, /genre/romans/biographies
etc.On peut sĂ»rement faire mieux comme catĂ©gorisation. Ăa vaut le coup dây rĂ©flĂ©chir davantage que ce que jâai fait en rĂ©digeant cette pageâŠ
DâaprĂšs notre dictionnaire de donnĂ©es, nos modĂšles doivent disposer de champs personnalisĂ©s. Custom Fields.
Pour cela, nous allons utiliser lâindispensable plugin ACF
Leur ancien logo
Pratique.
Consulter lâexcellente documentation du plugin (final user et dev)
Par défaut, ACF chercher un répertoire acf-json dans
votre thĂšme oĂč sont stockĂ©s le ou les fichiers JSON. Ce dossier doit
ĂȘtre inscriptible par le serveur web (755).
Créer le répertoire acf-json
Donner les droits en Ă©criture Ă lâutilisateur de
votre serveur web (www-data avec Apache par ex.)
chown -R www-data:www-data /path/vers/montheme/acf-jsonEnregistrer les champs via lâadmin. Et voilĂ !
<?php
//Personnaliser le point de sauvegarde du JSON.
function enis_acf_json_save_point( $path ) {
return get_stylesheet_directory() . '/my-custom-folder';
}
add_filter( 'acf/settings/save_json', 'enis_acf_json_save_point' );
//Personnaliser le point de chargement du JSON.
function enis_json_load_point( $paths ) {
// Remove the original path (optional).
unset($paths[0]);
// Append the new path and return it.
$paths[] = get_stylesheet_directory() . '/my-custom-folder';
return $paths;
}
add_filter( 'acf/settings/load_json', 'enis_json_load_point' );https://www.advancedcustomfields.com/resources/synchronized-json/
Voir leur guide sur lâimplĂ©mentation dâassociations bidirectionnelles
$value = get_field( "text_field" );
if( $value ) {
echo wp_kses_post( $value );
} else {
echo 'empty';
}ACF PRO permet de crĂ©er des pages dâadministration directement depuis ACF.
Le dĂ©veloppement de pages dâadmin peut se faire :
add_menu_page() et
add_submenu_page() (dans un plugin);Permet de crĂ©er des pages dâoptions administrables avec les possibilitĂ©s suivantes :
options);Gain de temps énorme. TrÚs utile.
Créer une page;
Ajouter des champs personnalisés;
Intégrer la page à WordPress (dans un plugin !) :
<?php
add_action('acf/init', function() {
if( function_exists('acf_add_options_page') ) {
acf_add_options_page();
}
});Récupérer
les valeurs, avec les mĂȘmes fonctions (get_field,
the_field, etc.) que pour des champs ACF âclassiquesâ (mĂȘme
API !). Il suffit dâajouter en 2nd argument lâid de la page :
<?php
//Fournir
$variable = get_field('field_name', 'option');âoptionâ est lâid par dĂ©faut dâune page dâoptions. Voir la documentation de acf_add_options_page( [$settings] );
add_action('acf/init', function() {
//Vérifier que la plugin ACF PRO est activé
if( function_exists('acf_add_options_page') ) {
//id par défaut 'option'
acf_add_options_page(array(
'page_title' => 'Theme General Settings',
'menu_title' => 'Theme Settings',
'menu_slug' => 'theme-general-settings',
'capability' => 'edit_posts',
'redirect' => false
));
acf_add_options_sub_page(array(
// Définir la page parent via slug
'parent_slug' => 'theme-general-settings',
'page_title' => 'Theme Header Settings',
'menu_title' => 'Header',
));
acf_add_options_sub_page(array(
'parent_slug' => 'theme-general-settings',
'page_title' => 'Theme Footer Settings',
'menu_title' => 'Footer',
));
}
});Pour les options du site (générales) on peut utiliser la page Home pour stocker les custom fields.
Ajouter
des MetaBox custom sur nâimporte quel Ă©cran dâadmin (Admin site,
CPT, CT, Posts, etc.) avec les fonctions
add_meta_box et add_meta_boxes.
Permet de customiser nâimporte quel formulaire dâune page dâadmin, avec ou sans ACF. Câest cette API quâutilise ACF !
//Ajouter metabox (champ formulaire, ou juste une info en lecture seule)
function bp_book_register_metabox() {
add_meta_box(
'book_details',
'Détails du livre',
// Callback pour le rendu de la métabox (HTML/CSS/JS)
'bp_single_book_metabox_render',
'book',
'normal',
'default'
);
}
add_action('add_meta_boxes', 'bp_book_register_metabox');
// Fonction en charge du rendu de la metabox (markup)
function bp_single_book_metabox_render($post) {
$author = get_post_meta($post->ID, '_book_author', true);
// Ajouter un nonce (protection anti CSRF)
wp_nonce_field('book_details_nonce', 'book_details_nonce_field');
?>
<p>
<label for="book_author">Auteur :</label><br>
// Ajouter nos champs customs (input de form)
<input type="text" id="book_author" name="book_author"
value="<?php echo esc_attr($author); ?>" style="width:100%;">
</p>
<?php
}
// Traiter le formulaire
function bp_book_save_metabox($post_id) {
// Filtrer sur le champ de la metabox
if (!isset($_POST['book_details_nonce_field'])) return;
// Vérifier nonce
if (!wp_verify_nonce($_POST['book_details_nonce_field'], 'book_details_nonce')) return;
if (defined('DOING_AUTOSAVE') && DOING_AUTOSAVE) return;
// Validation
if (isset($_POST['book_author'])) {
update_post_meta($post_id, '_book_author', sanitize_text_field($_POST['book_author']));
}
}
add_action('save_post_book', 'bp_book_save_metabox');'Edit' du post type book
(listing), ajouter une colonne personnalisé affichant
le nombre de copies disponibles Ă lâemprunt pour chaque livre;'Edit' du post type book
(listing), permettre de trier les livres suivant le nombre de
copies disponibles (ASC). Pour cela, rendre la colonne
crée précédemment sortable.Abonné)