Bloc plugin : utiliser Ajax pour se connecter à une API distante - Formation Gutenberg - Capitaine WP

Formation « Créer des blocs avec Gutenberg »

Bloc plugin : utiliser Ajax pour se connecter à une API distante

Lecture :7 minutes •0

Dans la même lignée que le bloc Article, le bloc Plugin permet de récupérer les informations d’une extension à partir du site wordpress.org. On va donc étudier le code du bloc afin de voir comment cela fonctionne sous le capot.

Voici un bloc dont je suis plutôt fier car il trouve son utilité dans de nombreux blogs, notamment ceux qui présentent des extensions WordPress.

Il permet de rechercher une extension dans le répertoire officiel wp.org et d’afficher le même type de bloc que l’on a lorsque l’on ajoute une extension dans l’interface d’administration, rubrique Extensions :

Le bloc extensions
Afficher les informations d’une extension dans votre article

Le code de notre bloc se trouve sur Github dans le répertoire src/blocks/plugin pour la partie JS, mais aussi dans src/component/searchPlugin, et comme c’est un bloc dynamique il y a une partie PHP que vous trouverez dans classes/Blocks/Plugin.php. Enfin le template de rendu PHP est situé dans public/templates/plugin.php.

Code source sur Github

Analyse du code

Ok alors c’est parti, on commence par index.js dans lequel on retrouve un code très simple, et très proche du bloc article :

  • un seul attribut, ici le slug de l’extension retenue.
  • un composant <Preview /> pour gérer l’affichage dynamique du bloc

Dans inspect.js, c’est tout aussi simple : j’appelle mon composant <SearchPlugin />, qui, comme <SearchPost /> a été externalisé dans le but de pouvoir être aisément réutilisé.

Le composant de recherche des extensions

Allons voir dans src/components/searchplugin/index.js. On va retrouver ici une architecture très proche de celle que l’on avait pour <SearchPost /> (et <SearchProduct />).

J’ai un champ texte qui lance une fonction recherche, protégée par un Debounce, qui je le rappelle permet d’attendre 300ms pour vérifier que l’utilisateur a bien terminé sa saisie.

Ensuite, contrairement à la recherche d’article, ou j’utilisais l’API Rest interne de WordPress, là je vais faire une requête Ajax standard pour aller piocher dans l’API des plugins de WordPress, qui se trouve sur wp.org.

Mais voilà le souci : leur API Rest est un peu « mal fichue » : elle est mal documentée et ne renvoie pas tout le temps du JSON, ce qui est embêtant.

Après des heures de bataille infernale, j’ai abdiqué et j’ai décidé de passer par la fonction PHP plugins_api(), qui utilise cette même API mais qui récupère plus facilement les données. Il me restera ensuite simplement à faire un json_encode() vers mon JS.

En définitive, au lieu de faire une requête Ajax vers wp.org, je fais une requête Ajax vers mon PHP (via la méthode classique WordPress) et c’est mon PHP qui se connectera à l’API distante via la même fonction utilisée par WordPress dans l’interface d’administration, menu « Extensions ».

en JS, j’utilise la méthode fetch().then() qui je le rappelle est la version moderne pour faire une requête Ajax en JS, sans jQuery.

Le saviez-vous ?

Pour faire une requête Ajax à WordPress, il faut fournir l’URL spécifiquement conçue pour ces requêtes. Pour cela il suffit d’envoyer admin_url(‘admin-ajax.php’) via wp_localize_script().

D’ailleurs j’envoie cette information depuis classes/WP/Gutenberg.php.

L’API des plugins

On va maintenant aller voir du côté de PHP. C’est dans classes/Blocks/Plugin.php que ça se passe, notamment avec la fonction search_plugins() :

PHP
classes/Blocks/Plugin.php

Le premier paramètre de la fonction plugins_api() permet d’indiquer ce que l’on veut faire : lancer une recherche de nom d’extension, récupérer les informations d’une extension en particulier, récupérer les extensions populaires…

Tous les détails sont expliqués dans la documentation.

Pour ma part j’utilise pour l’instant query_plugins pour lancer une recherche. Il me faudra donc fournir le paramètre search dans le deuxième argument de la fonction. J’indique également que je souhaite 24 résultats (arbitraire), et je demande à l’API de me fournir tous les champs, car cette flemmarde me fournit pas l’icône ni le nombre d’installations actives par défaut.

Attention

La recherche d’extension nécessite à l’utilisateur de fournir un nom complet. Les recherches approximatives ne donneront rien. Essayez depuis votre interface d’administration, vous constaterez le même souci.

Notez qu’en début de fichier, j’importe plugin-install.php de WordPress afin d’accéder à la fonction plugins_api().

Enfin, j’ai une fonction prepare_datas() qui me permet de faire quelques manipulations sur les données reçues :

  • Décoder les entités HTML sur le titre et la description pour éviter les soucis d’encodage de caractères spéciaux
  • Récupérer l’icône de l’extension la plus adaptée (si la version 2x existe, je prend celle-ci, car plus grosse)
  • Remanier le nombre d’installations actives pour avoir « 1+ millions installations actives » ou « moins de 10 installations actives » comme le fait WordPress dans l’admin
  • Enlever le lien de l’auteur (car leur balise a n’intègre pas de _blank)
  • Transformer la note sur 100 en une suite d’étoiles jaunes SVG pleines, semi pleines et vides

Attributs et stockage, le dilemne

Dans mon code, je ne récupère que le slug de l’extension, que je stock dans le seul attribut slug. Auparavant je stockais toutes les informations, pour les avoir directement à portée.

Mais le souci, comme je l’expliquais dans le cours précédent, c’est que j’aurais alors des données « statiques » qui ne reflètent que la réalité à cet instant. Dans le futur la note et le nombre d’installation actives resteront les mêmes, ce qui obligerait l’auteur à refaire le bloc.

C’est pour cette raison que je ne conserve que le slug. Lorsque je l’envoie à la <Preview />, je vais devoir refaire un appel API pour récupérer les dernières données.

Pareil lors du rendu PHP. Mais ce n’est pas grave, cela me permet au moins d’avoir toujours les informations à jour.

Duplication de code

Le problème de cette approche c’est que je vais avoir malheureusement des doublons niveau code , notamment au niveau du template : j’ai un template en JS, pour l’aperçu dans Gutenberg, et le même en PHP, pour l’affichage sur le site.

Mais nous n’avons pas le choix. Pire encore, dans les premières version du code, j’avais un équivalent JS pour la fonction de préparation des données (les étoiles, les installations actives) mais du coup ce qui me sauve c’est que mes requêtes Ajax passant par PHP, j’ai pu tout centraliser.

Chose que je n’aurais pas pu faire si j’avais utilisé l’API Plugins en JS.

Information

Avec une approche hybride JS + PHP, parfois il est inévitable d’avoir des duplications de code.

La preview

On va finir sur le fichier src/blocks/plugin/preview.js. J’utilise componentWillMount() et ComponentDidUpdate() pour vérifier tout changement sur le slug de l’extension : si le slug change, je relance une requête API.

Le code est le même que dans <SearchPlugin />, à la différence prêt que je lance une autre fonction Ajax : get_plugin et non plus search_plugin.

Côté PHP (dans classes/Blocks/Plugin.php), la fonction va cette fois appeler l’API Plugin via le paramètre plugin_information : je ne recherche plus des extensions, je veux les informations d’une extension en particulier. J’indique donc explicitement le slugde l’extension désirée.

PHP
classes/Blocks/Plugin.php

Il me reste ensuite simplement à afficher ces informations dans le composant en JS. Je retourne voir le code dans preview.js. J’ai un if pour vérifier que les données sont bien arrivées. Si ce n’est pas encore le cas, j’affiche un message « chargement » avec le <Spinner />.

Une petite particularité cependant. Ma valeur dans stars est du HTML. Et React n’aime pas trop qu’on lui envoie du HTML pré-mâché. Il préfère qu’on lui envoie des données brutes.

Dans des précédentes versions j’envoyais la note brute sur 5, et j’avais une fonction qui affichait les étoiles correspondantes (dont le SVG était stocké dans un fichier JS à part, comme on l’avait vu dans le cours sur les icônes personnalisées).

Pour éviter la répétition de code, je réceptionne désormais le HTML prêt-à-l’emploi mais React m’empêche de l’insérer directement.

Pour m’en sortir je dois donc utiliser l’attribut dangerouslySetInnerHTML={ { __html: stars } }.  Cela lui permet de savoir que le HTML vient d’ailleurs, n’est pas sûr, du coup le système prend les précautions nécessaires.

Bon je vous accorde que le nom fait peur, mais c’est  une technique tout à fait légitime.

Advice

Si vous devez injecter du HTML déjà prêt dans React, vous ne pourrez pas le faire directement. Souvenez-vous d’utiliser l’attribut dangerouslySetInnerHTML. C’est la manière officielle de le faire.

Rendu côté PHP

Ici c’est le même fonctionnement que précédemment : je charge l’information via la plugins API, et je remplis le template qui se trouve dans public/templates/plugin.php.

Du coup, à chaque fois que quelqu’un affichera l’article, PHP générera cette page en allant piocher sur l’API wp.org. Il faut prendre en compte que cela peut légèrement ralentir le temps de rendu de la page.

Pour corriger ça je pourrais mettre en place un système de transient, ou laisser faire un système de cache comme WP Super Cache ou WP Rocket.


Comme on a pu le voir dans ce cours et le précédent, lorsque l’on réalise un bloc dynamique, mieux vaut ne stocker que l’identifiant de la ressource et récupérer ses informations au dernier moment. Cela permet de s’assurer que les données soient toujours à jour, même si pour cela il faut faire plusieurs requêtes API.

Dans le prochain cours on va voir le bloc Google Map, qui utilise une librairie JS externe sans poser aucun souci avec React.

0

Questions, réponses et commentaires

Laisser un commentaire