Formation « Développer un thème WordPress sur mesure »

Utiliser le hook « save post » pour créer un sommaire

Lecture : 6 minutes • 5

Dans le cours précédent, on a vu comment utiliser le hook save_post pour calculer le temps de lecture lors de l’enregistrement d’un article. On va maintenant voir un autre cas de figure : la génération d’un sommaire. Nouvelle subtilité ici puisqu’on va devoir modifier le contenu de l’article tout fraichement enregistré.

On va chercher à générer cette fois un sommaire cliquable, nous permettant de nous rendre rapidement à une section d’un article, comme c’est le cas sur ce site :

La capture montre le site Capitaine WP affichant un temps de lecture ainsi qu'un sommaire.
Le sommaire et le temps de lecture sont générés via le hook save_post

Le sommaire est un élément de design idéal pour les contenus longs. Et d’autant plus s’il est généré automatiquement en se basant sur les titres de la publication (H2, H3, H4…).

Aparté: en réalité…

Dans cette capture, le sommaire est en fait généré par un bloc Gutenberg que vous retrouverez dans mon extension Advanced Gutenberg Blocks. Celui-ci génère le sommaire à la volée via Javascript. Si le développement de blocs vous intéresse, consultez ma formation Gutenberg. Si par contre votre sommaire n’est pas dans le contenu, mais par exemple en barre latérale, il faudra donc mettre en application ce que l’on va voir dans ce cours.

En ce qui concerne les bases, on va utiliser la même technique que pour calculer le temps de lecture, en passant par le hook save_post.

PHP
functions.php

Vous noterez que j’ai créé une nouvelle fonction pour cela, mais j’aurais pu tout autant mettre le code que l’on va voir dans la même fonction que pour le temps de lecture. Après tout les conditions sont les mêmes.

Conseil

Mais en programmation, une fonction doit se concentrer sur une et une seule tâche en particulier.

Du coup, pour générer le temps de lecture et le sommaire, je préfère utiliser 2 fonctions différentes, qui seront basées sur le même hook.

Préparer le terrain : le sommaire et les ancres

Avant de se lancer corps et âme dans le code, on va déjà réfléchir à ce que l’on a besoin de faire.

Ce que l’on veut, c’est générer le sommaire et l’enregistrer dans les post metas. Ca maintenant, on sait faire. Mais pour que notre sommaire fonctionne, il va falloir que ses liens mènent quelque part.

Et pour cela on va devoir créer des ancres, c’est-à-dire des liens menant ailleurs au sein de la page. Par conséquent on a besoin du lien d’un côté, dans le sommaire, commençant par #, ainsi que de la cible de ce lien, l’ancre, dans le contenu. Elle s’exprime sous forme d’un ID, en attribut de la balise titre.

HTML

Donc générer le sommaire ne va pas suffire. Il faudra également modifier le contenu renvoyé par le hook, en ajoutant les identifiants des ancres sur chaque titre, et enfin réenregistrer ce contenu.

Avant de continuer, créez un article avec différents niveaux de titres et insérez du faux texte entre grâce à un générateur de Lorem Ipsum.

Un article contenant des titres et du faux texte pour faire nos tests
Créez un article contenant des titres de différents niveaux et du faux texte

Tâche 1 : Générer les ancres

Dans le but de générer des ancres, on va d’abord devoir analyser le contenu de notre article.

Dans la fonction lancée par le hook, on a vu qu’on a accès au contenu de l’article via le paramètre $post. Il va falloir maintenant le décortiquer pour trouver les titres.

Autant en JS on peut facilement analyser ou parser le DOM (les balises) d’un document, autant ce n’est pas évident en PHP. C’est pour cela qu’on va faire appel à la librairie Simple HTML DOM.

Téléchargez le fichier et placez-le dans votre thème, dans le dossier /lib/simple_html_dom.php par exemple.

Il faudra ensuite appeler cette librairie depuis notre fonction, juste avant d’exécuter notre code :

PHP
functions.php

Afin de voir si la récupération fonctionne, on va déjà tester ce code. Pour l’instant on parse le contenu avec file_get_html() , et on filtre pour ne garder que les titres grâce à $html->find().

On affiche ensuite les titres trouvés dans le log d’erreurs PHP (qu’on avait configuré au début de la formation) et qui se trouve dans wp-content/debug.log. Enregistrez votre article afin de lancer le hook.

Plain Text
wp-content/debug.log

Si tout a fonctionné, vous devriez avoir quelque chose d’équivalent. Si vous avez une erreur PHP, elle sera également affichée ici. Dans ce cas l’enregistrement devrait se solder par une erreur et l’éditeur vous l’indiquera via un bandeau rouge, sans donner plus de détails, d’où l’importance du log :

L'éditeur indique qu'il n'a pas pu enregistrer l'article via un bandeau rouge
Si vous voyez ce message, c’est que vous avez une erreur dans votre code

Créer les ancres et mettre à jour le HTML

On va maintenant pouvoir générer le lien de l’ancre et l’injecter dans le contenu.

PHP
functions.php

Afin de nettoyer les caractères spéciaux et avoir une URL propre, on utilise la fonction de WordPress sanitize_title(). Mon Titre deviendra mon-titre.

Ensuite, on met à jour l’élément (le titre) en lui ajoutant un identifiant (qui permet de créer l’ancre).

Bien. Maintenant il faut enregistrer à nouveau l’article. Vous allez me demander si du coup il n’était pas judicieux de faire tout ça avant l’enregistrement initial, car là au final on enregistre une seconde fois. Eh bien ce n’est pas possible car le hook save_post ne s’exécute qu’après l’enregistrement et il n’existe pas d’autre hook pour le faire avant.

Attention à la boucle infinie !

Et là on va faire face à un problème : pour enregistrer l’article on va utiliser wp_update_post(), qui va appeler le hook save_post, qui va appeler notre fonction, qui va appeler wp_update_post()… Vous voyez où je veux en venir ? On vient de créer une boucle infinie !

Pour éviter ça, on va donc provisoirement couper notre hook via remove_action(), pour le réactiver tout de suite après.

Pour voir si ça a fonctionné, vous allez devoir recharger la page de votre éditeur (car le contenu n’est pas mis à jour en temps réel suite à notre hook). Cliquez ensuite sur un titre et depuis l’inspecteur à droite, regardez dans Avancé. Vous devriez apercevoir votre ancre.

Chaque titre possède désormais une ancre HTML, affichée dans l'inspecteur à droite, rubrique Avancé
L’ancre a bien été générée pour chaque titre

Tâche 2 : Générer le sommaire

Maintenant que nos ancres ont été ajoutées et notre contenu réenregistré, on va devoir créer une post meta contenant la liste de nos titres, et leurs niveaux.

PHP
functions.php

En premier lieu on va créer une variable $summary qui va stocker le HTML de notre sommaire. Dans la boucle, on y insère pour chaque titre un nouveau lien qui commence par #.

Et à la fin, on enregistre la donnée dans une meta nommée summary.

Attention

Dans cet exemple je suis volontairement resté simple : du coup tous les éléments du sommaire s’afficheront de la même manière sans distinction entre les niveaux de titres.

Afficher le sommaire dans le template

Maintenant, on va tenter d’afficher notre sommaire afin de vérifier s’il marche correctement. De la même manière que pour le temps de lecture, on va utiliser cette fonction dans notre template single.php :

PHP
single.php

Ajoutez un peu de CSS pour obtenir un résultat plus joli, et vous devriez obtenir quelque chose comme ça :

Le sommaire généré apparait désormais sur la page de l'article.
Le sommaire apparait sur la page !

En cliquant sur les liens, vous devriez être amené directement à la bonne section !

Créer un défilement animé en CSS (Smooth Scroll)

Pour en terminer avec ce sommaire, ce serait encore plus sympa le défilement (ou scroll en anglais) pouvait être animée. Non pas juste pour être joli, mais aussi pour donner un indice visuel sur ce qu’il se passe : on n’a pas changé de page, on est juste allé plus bas.

Voici le code à utiliser dans votre CSS :

CSS
style.css

La propriété scroll-behavior permet d’activer le smooth scrolling dans votre page sans utiliser Javascript. La propriété scroll-margin-top, appliquée au titre, permet de laisser un peu d’espace lorsque le scroll est terminé : au lieu que le titre soit collé tout en haut de l’écran, il y aura 40px d’espace.

C’est d’ailleurs très pratique pour éviter que votre titre ne finisse sous un menu fixe.


Vous savez désormais tirer pleinement partie du hook save_post. Il vous sera utile à de nombreuses reprises dans le développement de vos thèmes.

Pour terminer, il existe pleins d’autres hooks intéressants dans WordPress, n’hésitez pas à faire un tour sur la documentation à ce sujet.

5

Questions, réponses et commentaires

  1. Fabrice

    Le 29 juin 2020

    Bonjour capitaine,

    J’aurais voulu connaître le but de faire le remove_action/wp_update_post/add_action dans la boucle.

    Est-ce-qu’il ne serait pas mieux de le faire après ?

    En tout cas, super site, supers tutos, je recommande !!

    1. Maxime BJ

      Le 29 juin 2020

      La fonction update_post() appelle le hook save_post(). Du coup, si tu n’interrompt pas le lancement de ce hook à ce moment, tu créées une boucle infinie.

      1. Fabrice

        Le 3 juillet 2020

        Bonjour Maxime,

        Je me suis mal expliqué.

        Je connais ce piège, je suis tombé dedans quand j’ai commencé à faire du dev sur WP. Jolie perte de temps à l’époque, l’appel des hook est invisible en soi, et donc quand tu ne connais pas le mécanisme interne de WP, tu ne trouve pas tout de suite d’où vient le problème.

        Ma question concernait le fait de faire le save dans la boucle. En fait, tu fais un enregistrement à chaque Hx trouvé. Est-ce-qu’il ne faudrait pas plutôt ne faire qu’un seul save après la boucle ou est-ce-qu’il y a un intérêt caché ? (temps de sauvegarde par exemple ?)

        1. Arnaud ZELER

          Le 7 août 2020

          Je pense moi aussi qu’il serait plus judicieux d’effectuer le wp_update_post() ) la fin de la boucle $html->find( ‘h2, h3, h4’ ). Mais sinon à part ça, super auto pour comprendre le fonctionnement du hook save_post. Merci !

          1. Maxime BJ

            Le 10 août 2020

            Oui en effet ce serait bien plus judicieux, je vais corriger ça au plus vite.

Laisser un commentaire