WordPress Ajax - Une approche orienté objet

Illustration de l'article

Aujourd'hui j'aimerai vous partager une approche orienté objet pour gérer l'ajax avec WordPress

La façon "Classique"

La façon "Classique" est le fait de mettre le code dans functions.php et ça ressemble à :

<?php
function add_js_scripts() {
    wp_enqueue_script( 'script', get_stylesheet_directory_uri().'/js/script.js', array('jquery'), '1.0', true );

    // pass Ajax Url to script.js
    wp_localize_script('script', 'ajaxurl', admin_url( 'admin-ajax.php' ) );
}
add_action('wp_enqueue_scripts', 'add_js_scripts');

add_action( 'wp_ajax_my_action', 'my_action' );
add_action( 'wp_ajax_nopriv_my_action', 'my_action' );

function my_action() {
    $param = $_POST['param'];
    echo $param;
    die();
}

Puis on appellerai notre action avec du javascript de cette manière (ou d'une autre) :

$.ajax({
    url: ajaxurl,
    method: 'POST',
    data: {
        action: 'my_action',
        username: 'Frast',
    },
    success: function (response) {
        console.log(response);
    },
});

Cette approche a toujours fonctionné (et fonctionnera toujours je pense), mais on peut constater plusieurs désavantages en utilisant une approche procédural, tels que :

  • Ça pollue le functions.php, que se passe t-il lorsqu'on a 2, 5, 15 actions pour l'ajax ? (Pour éviter ça j'ai toujours séparé mes actions dans des fichiers différents, ça permet une meilleure organisation également)
  • Cela nécessite d'éditer beaucoup de textes (nom de la fonction, le nom du fichier javascript aussi peut-être, le nom de l'action, ...) dans le cas où le nom de l'action doit changer
  • Vous allez dupliquer le même code la plupart du temps
  • Vous allez envelopper votre code dans certaines conditions si vous souhaitez le charger seulement dans certains scénarios (si on est sur la page de connexion seulement, si on est sur les articles seulement, ...)
  • Si vous avez plus d'une action et que vous appellez wp_localize_script() plus d'une fois cela créer de la duplication de code, ce qui n'est pas très utile (dans le cas de l'ajax, vous allez presque toujours le lier à admin-ajax.php)
  • ...

Vers l'orienté objet

Pour simplifier tout ça, j'ai fait une petite librairie que vous pouvez installer avec composer :

composer require frast/wp-aoo

(C'est que 2 fichiers php pour être honnête)

Une fois installé, vous pouvez aller dans votre functions.php et require l'autoloader de composer comme ça :

require(__DIR__ . '/vendor/autoload.php');

Pour la prochaine partie, je vais partir du principe que vous avez créé un dossier appelé src/ dans votre thème et à l'intérieur de celui-ci un dossier Ajax/ et enfin à l'intérieur de ce dernier un fichier MyAction.php.
Egalement, pour des raisons d'organisation, vous allez créer un autre fichier appelé loadAjax.php dans le dossier src/ et require ce fichier depuis votre functions.php (après le require de l'autoloader).
Enfin, vous allez créer un fichier javascript qui sera lié à l'action ajax, on l'appellera MyAction.js.

Votre architecture de thème doit ressembler à ça :

- assets/
    - js/
        - MyAction.js
- src/
    - Ajax/
        - MyAction.php
    - loadAjax.php
- vendor/
    - frast/
    - autoload.php
- functions.php

A l'intérieur de la classe MyAction, le code le plus basique peut être :

<?php
# src/Ajax/MyAction.php

namespace App\Ajax;

use Frast\AjaxHandler;

class MyAction extends AjaxHandler
{
    /** @var array I specify that my js script will need jQuery as a dependency */
    protected $dependencies = [
        'jquery',
    ];

    /**
     * Return the path of the javascript file binded to the action
     */
    public function getAssetSrc(): string
    {
        return get_stylesheet_directory_uri() . '/assets/js/MyAction.js';
    }

    /**
     * How the action is going to handle the requests
     */
    public function treatment(): void
    {
        // Do your stuff here, don't forget to sanitize user inputs in real case scenario
        $username = $_POST['username'];

        // JSON Response sent back
        wp_send_json([
            'message' => "Hello {$username} !",
        ], 200);
    }
}

A l'intérieur de MyAction.js :

// assets/js/MyAction.js

jQuery(document).ready(function () {
    // console.log(WP_ADMIN_AJAX);
    // console.log(MyActionNonce);

    jQuery.ajax({
        url: WP_ADMIN_AJAX, // This is globally available, it resolves to the admin ajax url
        method: 'POST',
        data: {
            // The action must be the same as the name of your php class
            action: 'MyAction',
            // For security reason, you must specify the nonce created for your php class
            // You can get it by suffixing "Nonce" to your php class, like so
            nonce: MyActionNonce,
            // Then send whatever data you like
            username: 'Stranger',
        },
        success: function (response) {
            console.log(response);
        },
    });
});

Enfin, n'oubliez pas de charger votre classe MyAction, mettez cela dans loadAjax.php :

<?php
# src/loadAjax.php

use Frast\AjaxLoader;
use App\Ajax\MyAction;

// We register our ajax handlers
(new AjaxLoader())
    ->register(MyAction::class)
    ->load()
;

Vous pouvez essayer cela tel quel, cela devrait fonctionner ! (normalement :p)
D'ailleurs, n'oubliez pas de spécifier à composer d'autoload également les fichiers de votre application dans le composer.json, voici un exemple de configuration :

{
    "require": {
        "frast/wp-aoo": "^1.0"
    },
    "autoload": {
        "psr-4": {
            "App\\": "src/"
        }
    }
}

Par la suite, rafraîchir les fichiers de composer avec la commande composer dump-autoload
Vous pouvez trouver une démo complète du package ici

Comment ça marche

La classe MyAction déclare 2 méthodes :

  • getAssetSrc() : Retourne le nom du fichier javascript qui est lié à l'action
  • treatment() : Traite la requête faites à l'action

La où normalement on créé les fonctions wp_ajax_nopriv_my_action et wp_ajax_my_action, MyAction hérite de Frast\AjaxHandler ce qui les créé automatiquement pour vous, basé sur le nom de la classe que vous lui avez donné.
Cela va également appeler la fonction wp_enqueue_script() avec les arguments que vous avez défini. (voir customisation plus bas)

Après on charge notre action à l'intérieur de loadAjax.php avec :

(new AjaxLoader())
    ->register(MyAction::class)
    ->load()
;

Cela va enregistrer toutes nos classes d'actions et les charger pour que WordPress puisse les utiliser. (le loader s'occupe de la partie add_action() et wp_enqueue_script() avec la configuration que vous avez donné pour chaque classe)

Tout le code est disponible ici et est gratuit pour n'importe quelle utilisation

C'est une librarie relativement petite, cela n'est pas très compliqué de comprendre comment elle fonctionne.

Pourquoi l'utiliser ?

En utilisant cette approche on bénéficie de :

  • Une façon propre d'organiser les fichiers
  • La plupart du temps la classe d'action reste petite, dans le cas où elle grandi, la logique reste dans une classe bien définie (mais gardez en tête de rester un peu SOLID)
  • Si un autre développeur doit rejoindre le projet, il peut voir instantanément quels sont les actions chargées, quels fichiers js sont utilisés, ...
  • Vous pouvez préciser des conditions de chargement (voir customisation plus bas)
  • Vous pouvez spécifier de façon claire les dépendances, version et in_footer si besoin (voir customisation plus bas)
  • Vous pouvez automatiser encore plus les choses, par exemple; charger automatiquement le fichier js par rapport au nom de la classe d'action

Cela vous encourage également à faire un peu plus d'orienté objet à l'intérieur de WordPress, étant donné que beaucoup de tutoriaux sont basés sur du procédural.

Customisation

Parfois, on souhaite charger notre classe d'action seulement selon certaines conditions.
Vous pouvez le faire en déclarant une nouvelle méthode dans votre classe de cette manière :

/**
 * Based on the return value, it will enable or not the ajax action
 */
public function conditions(): bool
{
    // You can also access Wordpress functions from here
    $canLoad = is_home();

    return $canLoad;
}

Selon la valeur retourné (de type booléen), cela va charger ou pas l'action. Cela permet d'économiser du temps d'interprétation ainsi qu'une requête serveur (étant donné qu'il ne va pas exécuter la fonction wp_enqueue_script() pour charger votre script js)

A noter, étant donné qu'on peut spécifier des arguments optionnels pour wp_enqueue_script(), vous pouvez les définir avec :

/** @var array */
protected $dependencies = [];

/** @var string|bool|null */
protected $version = false;

/** @var bool */
protected $inFooter = true;

A l'intérieur de votre classe d'action.

Vous pouvez override n'importe quel élément depuis votre classe d'action si besoin.

Conclusion

Ce package peut vous aider pour mieux organiser vos fichiers et vos logiques dans des projets conséquents (bien que je l'utilise pour des petits projets également) et c'est un bon point pour la maintenance des projets !

J'ai aussi créé une démo disponible ici qui contient quelques exemples d'utilisation du package.

J'espère que vous pourrez trouver quelconque utilités pour cette petite librairie et que ça vous motive à vous intéresser un peu plus à l'orienté objet si ce n'est pas déjà le cas ! :)


Laisser un commentaire