ENI Service - WordPress - PHP

Paul Schuhmacher

Durée : 28h

Novembre 2025

Partie 3 : Les API de WordPress

Partie 3 : Les API de WordPress #

Objectifs : Comprendre et savoir utiliser :

Savoir utiliser ces API permet de développer des thèmes (parent ou enfant) sur mesure et d’implémenter de nombreuses fonctionnalités !

Thème fil rouge #

Pour explorer toutes ces API nous allons développer un thème sur mesure.

Nous allons développer un site dédié à la consultation et à la gestion d’une bibliothèque.

Remarque : Par soucis de simplicité et au regard du temps imparti, certaines fonctionnalités seront intégrées directement dans le thème plutôt que dans des plugins. Les bonnes pratiques d’architecture d’une application WordPress seront abordées dans la suite de la formation. N’hésitez pas à faire des suggestions !

Remarque #

Thème fil rouge : Spécifications #

Le site doit :

Thème fil rouge : Rôle et permissions #

Thème fil rouge : Spécifications du système d’emprunt et simplifications #

Thème fil rouge : Spécifications du système de log et de notifications #

Thème fil rouge : Modèles de données #

bg right contain

Règles de gestion:

Créer un nouveau thème 1/2 #

Dans wp-content/themes :

  1. Créer le dossier library;

  2. Y créer les fichiers requis pour un thème :

    1. index.php ;
    2. style.css.
  3. Placer les métadonnées nécessaires dans style.css :

    /*
    Theme Name: Bibliothèque
    Author: John Doe
    Description: Un site de bibliothèque municipale
    Version: 1.0
    License: GPL
    Text Domain: BM
    Tags:
    */

Créer un nouveau thème 2/2 #

  1. Dans index.php :

    <?php
    
    var_dump("index.php : Bibliothèque municipale");
  2. Activer le thème.

Sitemap (Visiteur) #

home 
    - Liste des ouvrages 
        - Détail d'un ouvrage
    - Liste des auteur·ices 
        - Détail d'un·e auteur·ice
    - Liste des éditeurs  
        - Détail d'un éditeur
    - Blog
        - Détail d'un post
    - Categories
        - Term
            - Détail d'un ouvrage
    - À propos
    - Login
    - Création de compte

Sitemap (Abonné) #

home 
    - Liste des ouvrages 
        - Détail d'un ouvrage
    - Liste des auteur·ices 
        - Détail d'un·e auteur·ice
    - Liste des éditeurs  
        - Détail d'un éditeur
    - Blog
        - Détail d'un post
    - Categories
        - Term
            - Détail d'un ouvrage
    - À propos
    - **Mon compte** (consulter les livres empruntés)
    - Login
    - Création de compte

Sitemap et templates WordPress #

home (front-page) 
    - Liste des ouvrages (archive)
        - Détail d'un ouvrage (single book)
    - Liste des auteur·ices (archive)
        - Détail d'un·e auteur·ice (single author)
    - Liste des éditeurs  (archive)
        - Détail d'un éditeur (single editor)
    - Categories (taxonomy)
        - Term (archive-term)
            - Détail d'un ouvrage (single book)
    - Blog (archive des posts)
        - Détail d'un éditeur (single post)
    - À propos (single)

    - Login (fourni, à custom)
    - Création de compte (fourni, à custom)

Préparation des templates #

Comme vu au module 01, WordPress analyse l’URL pour construire un contexte (récupérer les données pertinentes auprès de la base) et choisir un template pour rendre ces données.

La Template Hierarchy est la stratégie employée par WordPress pour sélectionner le template PHP adapté à la ressource demandée.

Bien la comprendre est indispensable pour développer un thème et bénéficier du framework.

Source (à garder auprès de soi quand on développe des templates)

#

bg contain

Template hierarchy : du type de ressource (URL) au template, via des fallbacks #

w:900

À lire de gauche (input via URL => type de contenu) à droite ( => template choisi par WordPress core)

Fonctionnement et intérêt #

Fonctionnement

Intérêt

S’essayer à la template hierarchy avec les taxonomies natives (category et tag) #

  1. Créer un template archive.php :

    <?php
    var_dump(__FILE__);
  2. Visiter l’URL /category/uncategorized/;

  3. Créer un template category.php;

  4. Vérifier que la template hierarchy est bien respectée;

Exercice #

  1. Via le dashboard, créer un tag “Coup de coeur”, qui aura pour slug coup-de-coeur;
  2. Se rendre à l’URL /tag/coup-de-coeur;
  3. Créer un template dédié aux tags (tag.php), puis uniquement dédié au tag coup-de-coeur.
  4. Quel template sera utilisé si l’URL /tag/rentree-litteraire-2025 est demandée ?

Correction #

  1. Créer le template tag.php :

    <?php
    var_dump(__FILE__);
  2. Accéder à l’URL /tag/coup-de-coeur avec votre navigateur favori”;

  3. Créer le template coup-de-coeur.php :

    <?php
    var_dump(__FILE__);

Vérifier l’URL /tag/coup-de-coeur. Il doit s’afficher le chemin du template “tag-coup-de-coeur.php”; 4. Le template tag.php, qui sert de fallback car le template rentree-litteraire-2025.php n’existe pas.

Créer les pages Home et Blog (articles) #

w:1200

Configurer la page d’accueil et la page des articles #

w:800px

Configuration la plus utilisée, sauf si vous faites un blog ou voulez mettre en avant uniquement des articles. On pourra évidemment ajouter des articles sur la home si on le souhaite.

Template de la page d’accueil #

bg right contain

La page d’accueil est sur l’url racine (/).

On peut utiliser les templates suivants :

Le cas des templates custom page #

bg right contain

Ces templates ne sont pas scannés par défaut par le core.

Il faut créer le template, par exemple custom-home.php et ajouter un header Template Name: <nom> dans le fichier :

<?php
// Template Name: Mon template page d'accueil

Dans le dashboard d’édition d’une page, la métabox Page Attributes affiche une option Template qui permet de sélectionner un template pour rendre la page.

Peut être très pratique ! Si besoin d’un template unique pour un contenu, ou permettre aux admins de choisir entre différents templates !

Réécriture des URL des taxonomies par défaut #

bg right contain

Par défaut, les path des taxonomies sont :

On peut les modifier dans l’écran Permalink Settings du dashboard (recommandé).

Mettre categories et tags (au pluriel), pour uniformiser les noms des urls.

Une ressource de type collection devrait être au pluriel.

Résumé : URL et templates du thème pour le moment #

URL Ressource Template
/ Page d’accueil front-page.php
/news Actualités (Liste des posts) home.php
/categories/{term} Liste des archive.php
/tags/{term} Liste des tag.php
/tags/best-seller Liste des best-sellers tag-best-seller.php
Autre Ressource inexistante (erreur client) 404.php

Vérifier #

Les URL suivantes devraient appeler les bons templates (afficher un message dans chaque template, par exemple echo __FILE__;) :

Tester /tags/foo. Que remarquez-vous ?

=> Snapshot : tag v0.1 <= #

Dépôt kit-dev : avec git, se rendre sur le tag v0.1 pour récupérer le thème à ce stade de développement :

git clone https://github.com/paul-schuhm/wordpress-php.git
cd wordpress-php
git checkout v0.1

Des snapshots réguliers du thème fil rouge seront fait pour voyager à tous les stades de développement.

Développer ses templates #

Une fois que la template hierarchy est comprise (garder la carte à disposition), il est facile de développer ses templates, en allant du plus général au plus spécifique.

Nous continuerons ce travail, mais d’abord il est temps de développer un template et d’y placer du contenu !

Générer une page complète (enfin) #

Nous allons développer le template home.php en charge de présenter la liste des articles (News).

<?php
/**
 *
 * Template du header du site.
 *
 * @package BM
 */
?>
<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="<?php bloginfo( 'charset' ); ?>">
 <meta name="viewport" content="width=device-width, initial-scale=1">
 <?php wp_head(); ?>
</head>
<body <?php body_class(); ?>>
 <header>
  <!-- Navbar (à venir) -->
 </header>

Inclure le header #

<?php
get_header();

Cette fonction inclut automatiquement le fichier header.php du thème.

<?php
/**
 * Template du footer du site
 *
 * @package BM
 */

?>

<footer>
  &copy; Copyright <?php esc_html_e( date( 'Y' ) ); ?>
 </small>
</footer>
<?php wp_footer(); ?>
</body>
</html>
<?php
get_footer();

Cette fonction inclut automatiquement le fichier footer.php du thème. Le footer contient généralement :

Chaque template de page #

Comme le footer et le header sont quasi toujours les mêmes sur toutes les pages, on appelera ces deux fonctions dans chaque template de page. Par exemple notre template home.php :

<?php
/**
 *
 * Template pour lister les actualités (posts)
 *
 * @package BM
 */

?>
<?php
get_header();
// Contenu à insérer ici (contenu de <body>)
get_footer();

Vous devez enfin voir réapparaître la barre d’admin grâce à l’inclusion du CSS et du JS injecté sur les hooks wp_head et wp_footer. Activer le plugin Query Monitor pour la suite si ce n’est pas encore le cas.

Parfois, on a besoin d’afficher temporairement une information supplémentaire, notamment dans le header (période de l’année, promo, offres spéciales, évènement, etc.)

Avec les fonctions get_header() et get_footer() changer de template de header ou de footer est très simple.

Par exemple, créer un fichier header-sale.php, le customiser et appeler get_header('sale'). WordPress va chercher le fichier header-sale.php et le charger :

//home.php
<?php
//Défini par un intervalle de temps entre deux dates par ex., ou un paramètre dans le dashboard
$is_sale_period = true; 
get_header( $is_sale_period ? 'sale' : '' );

La Loop WordPress #

La loop est au coeur d’un thème WordPress. Elle permet de contrôler comment le contenu est affiché. C’est le composant qui fait le lien entre le contexte (requêtes SQL réalisées par le contrôleur WP::main() en se basant sur l’URL) et les templates (HTML).

On utilisera la loop dans tous les templates :

Un contexte par défaut est préparé par le contrôleur. Par exemple, sur l’URL /news de notre site, le contexte préparé contient la liste des posts.

Il est possible d’utiliser plusieurs loops par template, n’importe où pour étendre le contexte en fonction des besoins.

La Loop WordPress en pratique #

<?php
if ( have_posts() ) :
 while ( have_posts() ) :
  the_post();
   the_title( '<h2>', '</h2>' ); //ex. de template tag
endwhile;
endif;

Explications #

Pratique : Loop WordPress 1/2 #

  1. Placer la loop dans le template home.php;
  2. Utiliser le template tag the_permalink()pour placer le h2 du post dans une balise <a> afin de créer un lien pour afficher le contenu de l’article;
  3. Publier plusieurs articles (avec le plugin FakerPress);
  4. Afficher la page qui liste les posts (/news);
  5. Cliquer sur le lien d’un article. Que s’affiche-t-il ? Pourquoi ?
  6. En vous servant de la template hierarchy, créer le template approprié (le plus générique) pour prendre en charge le rendu d’un post;

Pratique: Loop WordPress 2/2 #

  1. Ajouter la loop WordPress dans le template en charge de présenter le contenu d’un post. Utiliser le template tag the_content() pour afficher son contenu principal :
    1. Le titre du post doit apparaître cette fois dans un header de niveau 1 (h1),
    2. Le titre et le contenu sont placés dans une balise HTML <main>;
  2. Tester en affichant le contenu d’un post;
  3. Afficher le code source de la page /news et vérifier la validité du HTML généré auprès du Markup Validation Service du W3C.

C’est un bon réflexe de vérifier le HTML produit par ses templates pour s’assurer de ne pas avoir oublié de fermer une balise par exemple.

Les templates tags #

Les template tags sont des fonctions du core WordPress pour afficher du contenu, notamment dans la loop WordPress. Ces fonctions offrent une interface claire pour afficher le contenu du site et customiser son affichage. C’est l’API pour extraire et afficher le contenu des posts dans les templates.

Par exemple, the_title() affiche (echo) le titre d’un post ou d’une page au sein de la loop. La fonction get_the_title() retourne le titre du post ou d’une page.

Il existe de nombreux template tags.

Template tags principaux #

Noter la convention de nommage :

Les template tags sont des fonctions, et la plupart d’entre acceptent des arguments pour personnaliser leur fonctionnement.

Voici la liste des principaux template tags.

On apprendre à en utiliser un certain nombre. Toujours se demander quand on a besoin d’accéder à une donnée “existe-t-il un template tag pour ça ?”

Content tags #

Pour manipuler/afficher le contenu des posts (post, CPT, page) :

the_permalink()     // Affiche l'URL du post
the_title()         // Affiche le titre du post
the_ID()            // Affiche l'ID du post
the_content()       // Affiche le contenu du post
the_excerpt()       // Affiche l'excerpt du post
the_date()          // Affiche le datetime de la publication du post
the_time()          // Affiche l'heure de la publication du post
the_author()        // Affiche l'auteur du post
the_tags()          // Affiche le tags du post
the_category()      // Affiche les catégories du post
edit_post_link()    // Affiche un lien d'édition du post uniquement si vous êtes connecté et autorisé à éditer le post
comment_form()      // Affiche un formulaire pour laisser un commentaire
wp_get_attachment_image()
wp_get_attachment_image_src()
post_class() // Affiche un ensemble de classes CSS générées par WordPress (très utile pour l'intégration)

Pagination #

WordPress fournit des template tags nécessaires pour gérer entièremement la pagination (génération du markup, création des liens, traitement des liens paginés) :

**paginate_links**
the_posts_pagination
the_posts_navigation
previous_posts_link
next_posts_link

paginate_links :

Exemple #

<?php
//Template d'archive (template home.php)

$posts_per_page = 5;

global $wp_query;

$args_pagination = [
    'type'      => 'plain',
    'next_text' => '=>',
    'prev_text' => '<=',
    'total'     => $wp_query->max_num_pages,
    'current'   => max(1, get_query_var('paged')),
];

echo paginate_links($args_pagination);

Copier/coller ce code dans home.php du thème library pour tester, avant get_footer().

Include tags #

Pour inclure des templates :

get_header()
get_footer()
get_sidebar()
get_template_part() // On y reviendra dans la séquence 5 sur le développement de thème
get_search_form()   // On y reviendra dans la séquence 5 sur le développement de thème
comments_template()

On retrouve nos fonctions get_header() et get_footer() !

Template parts #

Penser les templates comme une composition d’éléments réutilisables de template en template : composants. Utiliser les composants via le template tag get_template_part() :

//Fichier parts/list-item-book.php
//Template item de liste (Livre)
//Données : objet de type $BN_Book
<li>
    <h2><?php the_title(); ?></h2>
    //Etc
</li>

Depuis archive-book.php :

<?php
//Utiliser le template du composant (éviter appel à require + définir dépendance du template via $args !)
get_template_part(
 slug: 'list-item-book',
 args: array(
  'book' => $book,
 )
);

Taxonomies et termes #

single_cat_title()
the_category()
single_tag_title()
tag_description()
the_tags()
get_the_term_list()
the_terms()
the_taxonomies()

Site, assets tags #

Pour obtenir les informations du site et sur les assets pour construire des URL :

blog_info();
blog_info('name');
blog_info('admin_email');
blog_info('charset');
blog_info('language');
blog_info('rss_url');

get_stylesheet_directory_uri(); // URL de style.css du thème (racine du thème). Assets du thème
get_template_directory_uri();  // URL du template du thème. Assets du thème
admin_url(); // URL du site (admin)
home_url(); // Site Address (URL de la home), voir Settings/General
get_site_url(); // WordPress Address (URL de l'application WordPress), voir Settings/General


the_permalink() // a connaître
get_permalink() // a connaître
get_post_permalink()
get_page_link()
get_attachment_link()

edit_post_link()
get_edit_post_link()
get_delete_post_link()
edit_comment_link()
edit_tag_link()

get_search_url($s)
get_search_query()
the_feed_link()

Login tags #

wp_loginout()           // Génère automatiquement un lien Connexion ou Déconnexion selon l’état de l'utilisateur
wp_login_form()         // Affiche un formulaire de connexion complet et personnalisable
wp_register()           // Génère un lien Inscription ou Profil selon que l’inscription est activée et que l’utilisateur est connecté

wp_login_url()          // Retourne l’URL de la page de connexion
wp_logout_url()         // Retourne l’URL de déconnexion (avec nonce !)
wp_lostpassword_url()   // Retourne l’URL de récupération du mot de passe

is_user_logged_in()     // Renvoie true/false selon si un utilisateur est authentifié

En conclusion sur les template tags #

Requête principale (Main Query) #

bg contain right

Sous la loop, on trouve une instance de la classe WP_Query, en charge de la requête principal (Main Query).

La requête principale (faite à partir de l’analyse de l’URL, contexte) est réalisée via WP_Query->get_posts() et utilise l’objet global $wp_query.

Faire un var_dump($wp_query) sur l’URL d’un post pour voir concrètement cet objet (le fameux contexte)

Créer ses propres requêtes #

On peut créer ses propres requêtes pour récupérer du contenu soit :

Avec WP_Query #

$custom_query = new WP_Query([
    'posts_per_page' => 5
]);

Custom Loop avec WP_Query #

// Création d'une instance WP_Query
$custom_query = new WP_Query([
    'posts_per_page' => 5,         // nombre de posts à récupérer
    'orderby'        => 'date',    // tri par date
    'order'          => 'DESC'     // ordre décroissant
]);

// Utiliser les mêmes méthodes que pour la main query !
if ($custom_query->have_posts()) :
    while ($custom_query->have_posts()) : $custom_query->the_post();
        ?>
        <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
        <?php
    endwhile;
    // ! Important ! Repasse la main à la requête principale
    wp_reset_postdata();
endif;

Toujours appeler wp_reset_postdata() après une loop custom :

Que se passe-t-il quand on crée une requête custom ? #

Avec get_posts() #

$posts = get_posts([
    'posts_per_page' => 5
]);

Custom loop avec get_posts() #

$posts = get_posts([
    'posts_per_page' => 5
]);

foreach ($books as $post) {
    setup_postdata($post); //fait pointer $post global sur $post
    the_title(); // template tag travaille sur le post de get_posts
}
wp_reset_postdata(); // Restaure $post global pour Main Query

Pourquoi ne pas utiliser query_posts() ? #

//This is bad...
query_posts([
    'posts_per_page' => 5
]);

Conséquences

Bonnes pratiques #

API de WP_Query #

Explorons un peu l’API WP_Query. Pour écrire une requête il faut fournir au constructeur un tableau associatif PHP contenant l’ensemble des nos paramètres.

API de WP_Query, quelques exemples #

//Quelques exemples
$query = new WP_Query( array( 'category_name' => 'staff' ) );
$query = new WP_Query( array( 'category__not_in' => array( 2, 6 ) ) );

//Avec les métadonnées (Custom Fields !)
$args = array(
'meta_key' => 'color',
'meta_value' => 'blue',
'meta_compare' => '!='
);
$query = new WP_Query( $args );

Bonne pratique : déclarer un tableau $args en amont et le passer ensuite au constructeur de WP_Query.

API de WP_Query, exemple plus complexe #

// Display posts that have meta key ‘color’ NOT LIKE value ‘blue’ OR 
// meta key ‘price’ with values BETWEEN 20 and 100:

$args  = array(
 'post_type'  => 'product',
 'meta_query' => array(
  'relation' => 'OR',
  array(
   'key'     => 'color',
   'value'   => 'blue',
   'compare' => 'NOT LIKE',
  ),
  array(
   'key'     => 'price',
   'value'   => array( 20, 100 ),
   'type'    => 'numeric',
   'compare' => 'BETWEEN',
  ),
 ),
);
$query = new WP_Query( $args );

Pratique : WP_Query #

Sur la page /news, afficher :

  1. Uniquement les posts appartenant à la catégorie Important (à créer dans le dashboard);
  2. Les posts n’appartenant pas à la catégorie Important;
  3. Uniquement les posts d’un·e éditeur·ice précis (admin pour l’instant). Limiter à trois posts.
  4. Les posts par ordre alphabétique sur le titre;
  5. Les posts publiés après le 22/12/25 (exclus);
  6. Une liste paginée de tous les posts. Il doit y avoir 9 posts par page. Utiliser le template tag paginate_links() pour générer les URLs des pages paginées.

Générer des posts avec le plugin FakerPress au besoin

Écrire directement ses requêtes SQL avec $wpdb #

Utiliser l’objet global $wpdb, c’est l’API la plus bas niveau offerte par WordPress à la base de données.

$results = $wpdb->get_results(
 $wpdb->prepare(
  "SELECT ID FROM {$wpdb->posts} 
         WHERE post_type = %s AND post_status = 'publish' 
         ORDER BY post_date DESC 
         LIMIT %d",
  'post',
  5
 )
);

// Parcours et préparation des posts pour template tags
foreach ( $results as $row ) {
 $post = get_post( $row->ID );      // récupère l'objet WP_Post complet (avec ses métadonnées)
 setup_postdata( $post );           // initialise le $post global et les template tags
 the_title();
}

// Restaure le contexte global de la requête principale
wp_reset_postdata();

Éviter d’écrire directement des requêtes SQL brutes dans les templates #

Privilégier l’usage des requêtes SQL customs dans les plugins !

Variables globales #

global $post;
global $authordata;
global $current_user;

Utile parfois de travailler sur les variables globales pour accéderm au contenu brut issu de la base avant transformation via les hooks (notamment par les plugins). En cas de besoin, y accéder seulement en lecture, pas en écriture !

Gestion des menus (navigation) #

bg right contain

Système de gestion de menus :

  1. Déclarer des emplacements (locations) de menus auprès du core;
  2. Générer le markup HTML de la location dans les templates PHP (génération du markup) ;
  3. Créer un menu (contenu) via l’admin ou du code PHP;
  4. Associer le menu (contenu) à un emplacement.
  5. Le contenu du menu sera récupéré en base et rendu à l’endroit indiqué dans le template.

La distinction entre emplacement (location) et contenu est puissante et utile pour créer autant de menus que nécessaires et adaptés à chaque template ou plateforme ! Orienté final user (admin a le contrôle)

Créer des emplacements de menu et les charger auprès du core #

Créer des emplacements de menu (location) avec les fonctions register_nav_menus() et register_nav_menu().

<?php
/**
 * Initialise le thème
 *
 * @return void
 */
function bp_register_menus() {
/**
 * Créer plusieurs emplacements
 */
register_nav_menus(
 array(
  'primary'   => __(
   'Main navigation menu',
   'BP'
  ),
  'secondary' => __(
   'Secondary menu in left sidebar',
   'BP'
  ),
 )
);
}
//Hook pour executer ce code au bon moment (vu dans la suite)
add_action( 'after_setup_theme', 'bp_register_menus' );

Parenthèse sur functions.php ( #

Ce fichier est automatiquement chargé par le core. S’il est mal aimé c’est qu’il est souvent (mal) utilisé pour implémenter toutes les fonctionnalités du site.

Quoi mettre dans ce fichier ?

La création des menus a donc sa place dans le functions.php du thème car c’est propre à ce thème !

Exemple : Structure du thème #

mon-theme
    assets/
        main.min.css
        main.min.js
        img/
        fonts/
        ...
    inc/
        menus.php
        enqueue.php
        ...
    functions.php
    front-page.php
    header.php
    ...
    parts/

Exemple : functions.php typique #

<?php
require_once get_template_directory() . '/inc/enqueue.php';
require_once get_template_directory() . '/inc/menus.php';

function mon_theme_setup() {
    //Explication sur cette fonction slide suivante
    add_theme_support(
        'html5',
        array(
            'comment-list',
            'comment-form',
            'search-form',
            'gallery',
            'caption',
            'style',
            'script'
        )
    );
    //Ajoute des options d'admin pour appareance du site (Apparence/*)
    add_theme_support('custom-background');
    add_theme_support('custom-header');
    add_theme_support('custom-logo');
}
add_action('after_setup_theme', 'mon_theme_setup');

Fonctionalités de l’admin du thème avec add_theme_support #

La fonction add_theme_support permet de configurer des fonctionnalités supportées par le thème et son administration dans le dashboard: images mises en avant, menus, HTML5, custom-logo, etc. Voir la liste des options dans la doc.

) Fin de la parenthèse sur functions.php, retour aux menus #

Définir l’emplacement dans les templates #

Dans un template, pour générer le markup du menu, utiliser le template tag wp_nav_menu() :

//Dans header.php
 <header>
  <?php
  $args = array(
   'theme_location' => 'primary',
   'menu_class'     => 'nav-menu',
  );
  wp_nav_menu( $args ); // va chercher le menu (contenu) associé et produire tout le markup HTML
  ?>
 </header>

Comme la plupart des template tags, wp_nav_menu() accepte un grand nombre d’arguments pour customiser le HTML généré.

Il est également possible d’utiliser des hooks filter pour personnaliser le contenu des menus (souvent suffisant), ou même d’écrire sa propre classe (Walker_Nav_Menu) pour contrôler entièrement le HTML généré.

Créer le contenu du menu (Gestion de contenu, éditorial) #

bg right contain

Placer le contenu dans l’emplacement prédéfini (Gestion de contenu, éditorial) #

bg right contain

Association Menu(contenu) et Location

Exemples de personnalisation des menus #

//Afficher un menu différent pour un user connecté ou non en changeant de location dynamiquement
wp_nav_menu( array(
 'theme_location' => is_user_logged_in() ? 'logged-in-menu' : 'public-menu'
) );

//Préparer des emplacement de menu pour différentes tailles d'écran
wp_nav_menu( array(
 'header' => 'Main menu',   //Masqué sur mobile (via media query CSS)
 'footer' => 'Mobile menu', //Masqué sur desktop (via media query CSS)
) );

//Ajouter une classe à chaque élément du menu (via le hook 'nav_menu_css_class')
add_filter('nav_menu_css_class', function($classes, $item, $args, $depth) {
    $classes[] = 'ma-classe';
    return $classes;
}, 10, 4);

Pratique : Créer un menu pour le site #

  1. Créer un emplacement de menu principal dans le template header.php du thème;
  2. Créer un menu;
  3. Associer le menu à l’emplacement;
  4. Tester. Inspecter le HTML généré.
  5. En profiter pour compléter le template de la front-page. Elle doit afficher le contenu de la page Home.
  6. En utilisant la documentation, trouver un moyen de placer le menu dans un élément HTML <nav>;

Shortcodes #

L’API des shortcodes sert à insérer, dans le contenu d’un article ou d’une page, des fonctionnalités dynamiques générées par du code PHP via une syntaxe compacte de type [tag attribut=valeur][/tag].

Très pratique pour le final user. Les shortcodes sont adaptés pour :

API Shortcodes #

Créer un shortcode avec la fonction add_short_code() :

//tag 'spanc', fonction exécutée lors du traitement du shortcode, doit retourner le contenu
add_shortcode('spanc', function($atts = [], $content = null) {

    //Ne pas autoriser l'interprétation de shortcode pour certains roles
     if (!current_user_can('edit_posts')) {
        return ''; 
    }

    //Fusionner attributs fournis avec attribut par défaut.
    //Logique de validation des attributs à implémenter vous même !
    $atts = shortcode_atts(['color' => 'red'], $atts);

    return '<span style="color:' . esc_attr( $atts['color'] ) . '">' . esc_html( $content ) . '</span>';
});

Utiliser un shortcode #

Utiliser le shortcode n’importe où (page, post) :

[spanc color="blue"]Texte[/spanc]

Il faut que la partie du template appelle do_shortcode($shortcode_string). Ce qui est fait automatiquement dans les template tags the_content() et the_excerpt().

Un shortcode est une fonctionnalité indépendante du thème. Elle devrait donc être définie dans un plugin.

Hooks #

L’API la plus importante de WordPress ? Certainement!

bg right contain

Hooks : actions et filters #

C’est l’API la plus utilisée pour étendre le core de WordPress et créer votre site sur mesure.

Les Hooks (crochets), comme leur nom l’indique, sont des moyens d’executer du code à des moments spécifiques du cycle de vie de l’application WordPress.

C’est un mécanisme classique utilisé par les frameworks pour qu’ils puissent charger et utiliser notre code.

Les hooks permettent donc de modifier des comportements par défaut de WordPress.

C’est notamment l’API principale utilisée par les plugins pour interagir avec le core (plugin architecture)

Les hooks concrètement #

Un hook est un appel de fonction PHP avec plusieurs arguments :

<?php add_action($tag, $my_function_to_execute, $priority, $args)>
<?php add_filter($tag, $my_function_to_execute, $priority, $args)>

Hooks : actions et filtres #

Il existe deux types de hooks :

Actions #

Exemple #

Enregistrer une procédure (fonction PHP) lors de la publication d’un post :

add_action('publish_post', function($post_id) {
    // code exécuté quand un post est publié
    error_log("Post $post_id publié !");
});

On enregistre ici une fonction anonyme

Exemple : Log lors du changement de status d’un post #

Enregistrer une procédure (fonction PHP) lors de la publication d’un post

/**
 * Log chaque publication d'article
 *
 * @param string  $new_status L'id du post qui a changé de status.
 * @param string  $old_status L'ancien status.
 * @param WP_Post $post Le post concerné.
 * @return void
 */
function eni_log_on_post_change_status( string $new_status, string $old_status, WP_Post $post ) {
 $author_id = $post->post_author;
 if ( WP_DEBUG ) {
  error_log( "Post {$post->ID} Nouveau status : $new_status - Ancien status : $old_status. Auteur : $author_id. Titre : {$post->post_title}" );
 }
}

add_action( 'transition_post_status', 'eni_log_on_post_change_status', 10, 3 );

Ici enis_log_on_post_change_status est une string qui référence la fonction (type callable). Va être appelée par WordPress avec la fonction PHP call_user_func().

On a déjà appris deux hooks ! <!– Callables can be created in several different ways:

Closure object
string containing the name of a function or a method
array containing a class name or an object in index 0 and the method name in index 1
object implementing the __invoke() magic method

–>

Lire la documentation d’un Hook #

bg right contain

Par exemple, l’évènement transition_post_status:

Utiliser la documentation d’un hook pour écrire sa callback #

function enis_log_on_post_change_status( string $new_status, string $old_status, WP_Post $post ) {
    ...
}

Enregistrer sa callback #

add_action( 'transition_post_status', 'eni_log_on_publish_post', 10, 3 );

Pile d’appels et priorité #

Sur un même hook, plusieurs fonctions peuvent être enregistrées (celles du core, les vôtres, celles de plugins). Ces fonctions forment une pile. Quand un hook est exécuté (do_action()), toute les fonctions présentes dans la pile sont exécutée dans un ordre précis.

L’ordre de la pile d’appel est déterminée par :

Exemple de pile d’appels #

On enregistre 3 callbacks sur le hook init :

add_action('init', 'callback2', 10);
add_action('init', 'callback3', 10);
add_action('init', 'callback1', 5);

Ordre d’exécution :

Autres méthodes pour enregistrer des callbacks #

En PHP, les callables peuvent être créés de différentes façons :

Enregistrer la méthode d’un objet comme callback #

class ENIS_Logger {
 public function log_on_post_change_status( string $new_status, string $old_status, WP_Post $post ) {
       //...
 }
}
$logger = new ENIS_Logger();
// Appel avec une instance d'ENIS_Logger
add_action( 'transition_post_status', array( $logger, 'log_on_post_change_status' ), 10, 3 );

Utile si on veut utiliser le paradigme orienté objet, par exemple dans ses plugins.

Enregistrer la méthode statique d’une classe comme callback #

<?php
class ENIS_Logger {

    public static function log_on_post_change_status(string $new_status, string $old_status, WP_Post $post) {
       //...
    }
}

// Appel avec nom de la classe
add_action('transition_post_status', ['ENIS_Logger', 'log_on_publish_post'], 10, 3);

Si l’on utilise le nom de la classe, il faut que la méthode soit statique !

Enregistrer une callback sous forme de closure #

$some_data = 'Cette donnée est capturée par la closure avec la clause use';
// Closure (capture son environnement)
$log = function ( string $new_status, string $old_status, WP_Post $post ) use ( $some_data ) {
 $author_id = $post->post_author;
 if ( WP_DEBUG ) {
  error_log( "$some_data" );
 }
};
add_action( 'transition_post_status', $log, 10, 3 );

Executer une fonction namespacée sur un Hook #

Si la fonction est namespacée (car on développe un plugin par exempl !), il faut passer le nom complètement qualifié de la fonction (ou de la classe).

namespace MonPlugin;

function my_callback() {
    error_log('Hook déclenché !');
}

// Nom complètement qualifié. Utiliser la constante _NAMESPACE_ !
add_action('init', __NAMESPACE__ . '\\my_callback');

Attention à bien échapper le caractère  !

Executer une méthode d’une classe namespacé sur un Hook #

<?php
namespace MonPlugin;

class Logger {
    public static function log() {
        error_log('Hook déclenché via méthode statique !');
    }
}

// Nom complètement qualifié. Utiliser la constante _NAMESPACE_ !
add_action('init', [__NAMESPACE__ . '\\Logger', 'log']);

Les Hooks filtre (filters) #

Les filtres sont typiquement utilisés pour modifier du contenu avant de l’enregistrer en base de données, d’en générer le code HTML côté public (page web) ou côté admin (modifier les pages d’administration !), etc.

Forme (même que action) #

Pour enregistrer un filtre, utiliser la fonction add_filter() :

add_filter( string $hook_name, callable $callback, int $priority = 10, int $accepted_args = 1 );

Pour appliquer un filtre (et déclencher l’appel de toutes les fonctions présentes dans sa pile !), utiliser la fonction apply_filters() :

apply_filters( string $hook_name, mixed $value, mixed ...$args ): mixed;

Enregistrer et utiliser des filtres #

add_filter('my_filter', fn($v) => $v . ' B', 10);
add_filter('my_filter', fn($v) => $v . ' C', 10);
add_filter('my_filter', fn($v) => $v . ' A', 5);

// Résultat : "Ordre ? A B C"
echo apply_filters('my_filter', 'Ordre ?');

Exemples classiques #

// Avant enregistrement en base : pre_post_title
add_filter('pre_post_title', function($title) {
    return strtoupper($title); // le titre sera enregistré en majuscules
});
// Avant affichage HTML côté public du contenu d'un post
add_filter('the_content', function($content) {
    return $content . '<p>Texte ajouté automatiquement</p>';
});

// Côté admin : Ajouter une colonne custom à un tableau d'administration des posts

// Ajouter la colonne sur 'manage_posts_columns'
add_filter('manage_posts_columns', function($columns) {
    $columns['new_col'] = 'Nouvelle colonne';
    return $columns;
});

// Remplir la colonne (action) pour chaque post sur 'manage_posts_custom_column'
add_action('manage_posts_custom_column', function($column_name, $post_id) {
    if ('new_col' === $column_name ) {
        echo 'Valeur pour le post ' . $post_id;
    }
}, 10, 2);

// Appliquer un filtre (retourne une valeur)
$my_custom_title = apply_filters('the_title', '  My Custom Title (tm)  ');

Créer vos propres hooks ! #

On peut créer ses propres hooks ! Et c’est recommandé !

Cela permet d’utiliser la même logique que le core et de rendre son code plus modulaire et extensible. Pour cela il suffit de :

  1. Créer son propre évènement en y enregistrant des fonctions avec add_action() ou add_filter();
  2. Déclencher les évènements et leurs piles d’appels avec do_action() ou apply_filters().
<?php
add_action('monplugin_post_save', function($post_id, $post){
    //Action 1
}, 10, 2);
add_action('monplugin_post_save', function($post_id, $post){
    //Action 2
}, 10, 2);
//Etc...

//Déclencher la pile d'appels quelque part dans votre application WordPress (thème, plugins)
do_action('monplugin_post_save', $post_id, $post);

Précautions #

Connaître les évènements fournis par le core #

Concernant les hooks natifs, fournis par le core de WordPress, il faut les connaître. Ils correspondent à des moments clefs dans le cycle de vie de l’application (traitement d’un requête), du chargement du core à la production de la réponse HTTP.

La connaissance des hooks les plus utilisés et utiles vient vite.

Hooks principaux au cours du cycle de vie de l’application WordPress 1/3 #

Les hooks principaux, au cours du temps.

  1. Chargement initial

  2. Traitement des requêtes

Hooks principaux au cours du cycle de vie de l’application WordPress 2/3 #

  1. Rendu du thème

Hooks principaux au cours du cycle de vie de l’application WordPress 3/3 #

  1. Pages d’admin

Inspecter les hooks #

bg right contain

Avec le plugin Query Monitor :

Il y en a beaucoup. S’apprennent à l’usage. Avec l’expérience, le but est d’essayer de trouver le hook le plus précis , pour éviter des comportement inattendus dans le futur.

Hook actions les plus utilisés #

wp_head
wp_footer
wp_loaded
admin_head
init
admin_init
admin_menu
user_register
set_current_user
publish_post
save_post
pre_get_posts
create_category
add_meta_boxes_{posttype}
comment_post
switch_theme
wp_enqueue_scripts
admin_enqueue_scripts
template_redirect
login_enqueue_scripts
transition_post_status
template_redirect

Hook filters les plus utilisés #

the_content
the_title
the_permalink
post_class
body_class
wp_title
login_redirect
upload_mimes

Hooks liés aux users #

user_register
wp_login
wp_logout
profile_update

Développer avec les hooks (Hook first !) #

Exemple : Charger vos assets (public) via le hook wp_enqueue_scripts #

<?php
add_action('wp_enqueue_scripts', function() {
    // CSS
    $css_version = '0.1';
    wp_enqueue_style(
        'mon-plugin-front-css',
        get_stylesheet_directory_uri() . '/styles/main.min.css',
        [],
        $css_version //Toujours mettre une version à incrémenter pour forcer la recharge du cache côté client !
    );

    // JS
    $js_version = '1.2';
    wp_enqueue_script(
        'mon-plugin-front-js',
        get_stylesheet_directory_uri(__FILE__) . '/js/main.min.js',
        ['jquery'], // dépendances
        $js_version, //Toujours mettre une version à incrémenter pour forcer la recharge du cache côté client !
        array(
            'strategy'  => 'defer', // indiquer au navigateur que le script doit être exécuté après l'analyse du document
            'in_footer' => true, // où placer la balise script (ou utiliser async pour une exécution asynchrone.)
        )
    );
});

Conseil : Utiliser le hook wp_head pour précharger des assets critiques comme des fonts avec <link rel="preload">. Indique au navigateur de charger la ressource en priorité.

Exemple : Charger des assets CSS et JS côté admin sur une page custom #

add_action('admin_enqueue_scripts', function($hook) {
    // Charger seulement sur une page spécifique si besoin
    if ($hook !== 'toplevel_page_mon_plugin') {
        return;
    }

    // CSS
    wp_enqueue_style(
        'mon-plugin-admin-css',
        plugin_dir_url(__FILE__) . 'assets/admin.css',
        [],
        '1.0.0'
    );

    // JS
    wp_enqueue_script(
        'mon-plugin-admin-js',
        plugin_dir_url(__FILE__) . 'assets/admin.js',
        ['jquery'], // dépendances
        '1.0.0',
        true // placer en footer
    );
});

Exemple : Modifier l’ordre ou filtrer les posts dans un template #

Pour altérer la requête principale (Main Query) avant qu’elle ne soit exécutée, utiliser le hook pre_get_posts :

function monplugin_modify_home_query($query) {
    if ($query->is_main_query() && !is_admin() && $query->is_home()) {
        $query->set('post_type', array('post', 'movie'));
        $query->set('posts_per_page', 10);
    }
}
add_action('pre_get_posts', 'monplugin_modify_home_query');

Pour s’assurer d’exécuter ce hook dans un template :

Conclusion #

Nous avons vu dans cette séquence les API clés pour développer sur WordPress :

Pratique : Thème fil rouge #

Utiliser ces APIs pour continuer d’implémenter notre application WordPress pour la Bibliothèque.