Chapitre 16 sur 20

Créer et utiliser des endpoints AJAX

Laisser un commentaire

L’AJAX est un ensemble de technologies permettant d’effectuer des requêtes réseau et d’actualiser la page sans un rechargement complet. Utilisé à bon escient, cette technique permet de grandement améliorer l’expérience utilisateur en s’approchant de l’expérience offertes par des applications natives.

WordPress est utilisé la plupart du temps dans un paradigme client-server web classique : le navigateur envoie une requête au serveur, celui-ci génère le résultat via les templates et retourne au navigateur le code HTML correspondant à la requête.

Cependant, WordPress possède également une API REST complète et permet d’enregistrer des fonctions pour répondre aux requêtes AJAX. Ces deux méthodes permettent :

Sur le site d’Alphapole, l’AJAX est utilisé afin de permettre l’ajout d’un article au panier sans recharger la page. Nous allons ici aborder la manière dont on ajoute des fonctions AJAX.

L’usage de l’API REST est un tout autre sujet, il existe un handbook dédié à l’API REST. Par ailleurs, si vous souhaitez exploiter cette API en JavaScript, je vous conseille d’utiliser node-wpapi.

L’AJAX en back

Déclarer une fonction handler en back n’est en somme pas très complexe. Si l’on travaille dans le thème, on déclarera nos fonctions dans le functions.php. Par exemple, voici la fonction permettant de gérer l’ajout d’un produit au panier sur le site d’Alphapole.

function add_to_cart() {
    if (empty($_POST['product_id']) || empty($_POST['qt'])) return;
    WC()->cart->add_to_cart($_POST['productId'], $_POST['qt'], $_POST['variation_id']);
    wp_die();
}
add_action('wp_ajax_nopriv_add_to_cart', 'add_to_cart');
add_action('wp_ajax_add_to_cart', 'add_to_cart');

On voit que l’on déclare une fonction, dans laquelle on récupère des données en POST. On appelle ici une fonction de WooCommerce puis on termine l’exécution avec wp_die();.

Enfin on associe cette fonction à deux hooks : wp_ajax_nopriv_add_to_cart et wp_ajax_add_to_cart. Les préfixes wp_ajax_ et wp_ajax_nopriv_ permettent respectivement d’enregistrer la fonction pour les utilisateurs connectés et les utilisateurs non connectés.

Si vous n’enregistrez la fonction que pour wp_ajax_nopriv_, le endpoint AJAX sera inexistant pour les utilisateurs non connectés, et vice-versa.

Ici, le suffixe add_to_cart est ce qui va faire le lien entre le front et le back. En effet, le JavaScript envoie ce suffixe lors de l’appel AJAX, c’est ce qui permet à WordPress de savoir quelle fonction appeler.

Afin de revenir sur wp_die(), celui-ci permet de s’assurer que WordPress ne renvoie pas d’autre valeur que celle que renvoie la fonction. Si du HTML ou du texte brut venait à être ajouté à la fin de votre réponse, votre JavaScript serait très certainement mis en erreur.

L’AJAX en front

Du côté de votre front, il n’y a rien d’extraordinaire. Vous n’avez qu’une seule contrainte : envoyer les données avec l’encodage “multipart/form-data”. Mis à part cela, vous êtes comme à la maison.

Vous allez donc utiliser une des deux API disponibles dans les navigateurs, à savoir :

XMLHttpRequest est la méthode historique et présentait l’avantage d’offrir une plus grande compatibilité. On peut aujourd’hui considérer que fetch est adapté à tous les usages si l’on n’a pas à supporter de très vieux navigateurs (comme Internet Explorer).

Pour ma part, dans la mesure où on ne communique pas en JSON, j’ai une préférence pour le XMLHttpRequest car cette API dispose de l’interface FormData pour gérer le passage de valeurs en POST ou GET.

Ainsi, voici la fonction helper que j’utilise pour l’AJAX dans mes projets WordPress.

/**
 * Send XMLHttpRequest and call either error or success callback
 * @param { String } method
 * @param { String } url
 * @param { Object } body
 * @param { Function } errCallback
 * @param { Function } successCallback
 */
function ajax(method, url, body, errCallback, successCallback) {
    const xhr = new XMLHttpRequest();
    const form = body ? new FormData() : null;

    xhr.onload = () => {
        if (xhr.status !== 200) {
            errCallback(xhr.status);
            return;
        }

        successCallback(xhr.response);
    };

    xhr.onerror = errCallback;
    xhr.open(method, url);

    if (body) {
        Object.keys(body).forEach(key => form.append(key, body[key]));
        xhr.send(form);
    }

    else xhr.send();
}

Les fonction handlers de WordPress sont toujours en POST dans la mesure où elles doivent forcément contenir la clef action dans le corp de la requête.

Gérer les notifications WooCommerce

Par défaut, rien ou presque ne fonctionne en AJAX sur le front. Or WooCommerce possède un système de notifications :

  • produit ajouté au panier,
  • problème lié au stock,
  • problème avec l’adresse de livraison,
  • etc.

Lorsque des actions sont gérées en AJAX, les notifications restent dans une file et s’afficheront au prochain chargement de page. Pour éviter ce comportement non désirable, il faudra vérifier, récupérer et vider les notifications présentes dans cette file.

Pour cela, vous utiliserez les fonctions wc_has_notice, wc_notice_count, wc_get_notices et wc_clear_notices.

Par ailleurs, pour désactiver la notifications d’ajout au panier (vous le gérer directement en AJAX), vous pouvez utiliser le filtre suivant.

add_filter('wc_add_to_cart_message_html', '__return_false');

Il est possible d’utiliser cette fonction AJAX pour d’autres choses que les seuls endpoints AJAX que vous avez déclaré. C’est pourquoi je préfère conserver la possibilité de la méthode HTTP à utiliser.

Le endpoint est toujours /wp-admin/admin-ajax.php. Voici donc comment je gère l’ajout d’un article au panier côté JS.

function addToCart(productId, qt, variationId, callback) {
    ajax('POST', '/wp/wp-admin/admin-ajax.php', {
        action: 'add_to_cart',
        product_id: productId,
        variation_id: variationId
        qt,
    }, errHandler, callback);
}

Ici, ma fonction errHandler est une fonction générique qui affiche un message d’erreur tandis que la fonction callback est directement passée en paramètre à addToCart. Je peux par exemple dans cette fonction synchroniser d’autres éléments (le nombre d’articles dans le panier, le total…) ou invoquer d’autres requêtes (par exemple récupérer le HTML du mini panier).

Vous n’avez pas de contrainte sur le format des données renvoyées. Par exemple, il suffit d’utiliser echo avec json_encode en PHP afin de passer un tableau ou un objet en JSON. Il suffira de les décoder côté JS pour accéder aux données et actualiser l’interface.

Cependant, il est parfois plus avantageux de récupérer directement du HTML et de remplacer telle quelle une partie de la page. Il suffit dès lors d’utiliser innerHTML sur le parent de l’élément pour le mettre à jour d’un seul coup.

J’utilise par exemple la première technique sur la page panier pour récupérer les différentes mises à jour lors de l’actualisation des éléments du panier mais je préfère la seconde pour mettre à jour le mini panier.

Commentaires

Rejoignez la discussion !

Vous pouvez utiliser Markdown pour les liens [ancre de lien](url), la mise en *italique* et en **gras**. Enfin pour le code, vous pouvez utiliser la syntaxe `inline` et la syntaxe bloc

```
ceci est un bloc
de code
```