# AJAX dans l’admin WordPress : admin-ajax.php proprement, étape par étape

> Un tableau de bord qui se rafraîchit sans recharger la page, ça passe encore par admin-ajax.php. Voici comment le brancher sans se tromper de hook.

- Auteur : Clément Hadrot
- Publié le : 2020-01-29
- Mis à jour le : 2020-01-29
- Catégorie : Extensions
- URL : https://wpmoderne.dev.wordpress-developpement.fr/extensions/ajax-admin-ajax-php-etape-par-etape/

## L’essentiel

- Deux hooks à connaître : wp_ajax_ et wp_ajax_nopriv_
- wp_localize_script pour transmettre l'URL et le nonce
- wp_send_json_success et wp_send_json_error pour répondre proprement

Un client qui gère un catalogue de pièces détachées m'a demandé un tableau de bord d'administration capable de filtrer une liste de plusieurs milliers de références sans recharger la page à chaque clic. Pas question d'installer un framework JavaScript pour ça : `admin-ajax.php` suffit largement, à condition de comprendre les deux ou trois pièges qui font perdre une soirée entière quand on ne les a jamais rencontrés.

Ce fichier existe depuis les origines de WordPress et reste, en 2020, le point d'entrée le plus simple pour exécuter du PHP depuis du JavaScript dans l'admin. Il n'a rien d'obsolète : REST API est plus adaptée aux échanges front-end publics ou aux applications complexes, mais pour une action ponctuelle dans un écran d'administration, `admin-ajax.php` reste souvent la solution la plus rapide à mettre en œuvre.

## Déclarer les deux hooks wp_ajax_

Le principe tient en une ligne : WordPress route toute requête POST ou GET envoyée à `admin-ajax.php` avec un paramètre `action` vers un hook nommé `wp_ajax_{action}` pour un utilisateur connecté, et `wp_ajax_nopriv_{action}` pour un visiteur anonyme.

```
add_action( 'wp_ajax_filtrer_pieces', 'kaolin_filtrer_pieces_callback' );

function kaolin_filtrer_pieces_callback() {
    check_ajax_referer( 'kaolin_pieces_nonce', 'nonce' );

    if ( ! current_user_can( 'edit_posts' ) ) {
        wp_send_json_error( array( 'message' => 'Permission refusée.' ), 403 );
    }

    $reference = isset( $_POST['reference'] ) ? sanitize_text_field( wp_unslash( $_POST['reference'] ) ) : '';

    $resultats = kaolin_rechercher_pieces( $reference );

    wp_send_json_success( array( 'items' => $resultats ) );
}
```

Dans un écran strictement réservé aux administrateurs, il est tentant d'omettre `wp_ajax_nopriv_`. C'est justement la bonne pratique : si l'action ne concerne que des utilisateurs connectés, n'enregistrez que le premier hook. Ajouter le second par réflexe ouvre une porte inutile.

## Transmettre l'URL et le nonce au bon moment

Le script JavaScript ne connaît ni l'URL d'`admin-ajax.php` ni le nonce attendu par `check_ajax_referer`. C'est le rôle de `wp_localize_script`, appelé juste après `wp_enqueue_script`, qui injecte un objet global exploitable côté client.

> L'essentiel à retenir : Deux hooks à connaître : wp_ajax_ et wp_ajax_nopriv_ ; wp_localize_script pour transmettre l'URL et le nonce ; wp_send_json_success et wp_send_json_error pour répondre proprement

```
wp_enqueue_script( 'kaolin-admin-pieces', plugins_url( 'js/pieces.js', __FILE__ ), array( 'jquery' ), '1.0', true );

wp_localize_script( 'kaolin-admin-pieces', 'kaolinPieces', array(
    'ajaxUrl' => admin_url( 'admin-ajax.php' ),
    'nonce'   => wp_create_nonce( 'kaolin_pieces_nonce' ),
) );
```

Côté JavaScript, l'appel ressemble à ceci :

```
jQuery.post( kaolinPieces.ajaxUrl, {
    action: 'filtrer_pieces',
    nonce: kaolinPieces.nonce,
    reference: valeurSaisie
} ).done( function( reponse ) {
    if ( reponse.success ) {
        afficherResultats( reponse.data.items );
    }
} );
```

Une erreur fréquente : oublier que `action` doit correspondre exactement au suffixe du hook PHP, sans le préfixe `wp_ajax_`. Une faute de frappe ici renvoie une erreur 400 silencieuse, sans message explicite dans la console.

## Répondre avec wp_send_json_success et wp_send_json_error

Avant l'introduction de ces deux fonctions, il fallait construire soi-même un tableau avec une clé `success` et appeler `wp_send_json`. Elles font exactement cela, mais avec une convention stable que jQuery et `fetch` savent exploiter côté client sans code supplémentaire.

- `wp_send_json_success( $data )` envoie un objet `{ success: true, data: ... }` et termine l'exécution avec `wp_die()`
- `wp_send_json_error( $data, $status_code )` fait de même avec `success: false` et permet de préciser un code HTTP
- Les deux appellent `wp_die()` en interne : tout code placé après ne s'exécute jamais

C'est justement cette dernière ligne qui piège le plus de développeurs débutants : ils ajoutent un `return` après `wp_send_json_success()` en pensant arrêter la fonction, alors que l'exécution s'est déjà arrêtée à l'appel précédent. Le code n'est pas faux, simplement inutile.

## Vérifier les permissions avant de traiter la requête

`check_ajax_referer` vérifie que la requête vient bien du navigateur qui a chargé la page admin, pas qu'elle vient d'un utilisateur autorisé. Ce sont deux vérifications distinctes, et j'ai vu plusieurs extensions confondre les deux.

La règle que je retiens de ce projet : toujours enchaîner `check_ajax_referer` puis `current_user_can`, dans cet ordre. Le nonce protège contre le rejeu et la falsification de requête intersite, la capacité protège contre un utilisateur connecté mais non autorisé à effectuer l'action demandée.

> Un nonce valide ne prouve jamais qu'un utilisateur a le droit de faire une action : il prouve seulement que la requête vient du bon formulaire. Les deux contrôles sont complémentaires, jamais interchangeables.

## admin-ajax.php ou REST API : lequel choisir

Pour ce projet, la question s'est posée : fallait-il migrer directement vers un endpoint REST personnalisé ? La réponse a été non, pour une raison simple : l'action ne concernait qu'un unique écran d'administration, sans besoin d'authentification par clé d'API ni de réutilisation externe.

- Restez sur `admin-ajax.php` pour une action ponctuelle, interne à un écran d'admin, sans consommateur externe
- Passez à REST API dès qu'une application mobile, un site tiers ou un bloc Gutenberg autonome doit consommer les mêmes données
- REST API expose automatiquement une documentation de schéma et gère nativement les méthodes HTTP, ce qu'`admin-ajax.php` ne fait pas

Dans les faits, une bonne partie des extensions professionnelles continuent d'utiliser `admin-ajax.php` pour leurs écrans internes, tout en exposant une API REST pour les fonctionnalités destinées à être consommées ailleurs. Les deux mécanismes cohabitent très bien dans un même projet.

## Pour aller plus loin

Le couple `wp_ajax_` / `wp_ajax_nopriv_` reste, dix-sept ans après sa création, l'un des mécanismes les plus utilisés du cœur de WordPress. Sa simplicité est sa force : pas de schéma à déclarer, pas de route à enregistrer, juste une action et un callback.

Le prix de cette simplicité, c'est la rigueur qu'il faut s'imposer soi-même : nonce systématique, vérification de capacité, et sortie propre via `wp_send_json_*`. Sur ce projet, ces trois réflexes ont suffi à livrer un tableau de bord fluide, sans jamais toucher à la couche REST.
