WordPress et Ajax

L’interface d’administration de WordPress évolue. D’un point de vue technique l’utilisation d’Ajax se généralise, pour nous éviter les habituels et pénibles rafraîchissements de page. Les plugins doivent s’adapter, et proposer des interfaces cohérentes avec celle du moteur de blog. Ajax est donc de plus en plus présent dans les plugins.
Voici en quelques lignes, les bases du développement d’un plugin WordPress utilisant Ajax.

Ajax

Dans le cas d’une application PHP standard, nous avons le fonctionnement suivant:

  • Le navigateur envoie une requête http vers le site
  • Le serveur de ce site fait appel à l’interpréteur PHP pour exécuter une fonction PHP, qui va générer une page HTML
  • Cette page est renvoyée vers le navigateur qui l’affiche

L’avantage de la méthode est sa simplicité. Nous n’utilisons qu’un seul langage, HTML (ou PHP).Les interactions sont peu nombreuses, les pages sont générées en bloc, par rapport à un jeu de paramètres.

L’inconvénient est que, même si les changements ne concernent qu’une petite partie de la page, celle-ci est, malgré tout, régénérée, puis renvoyée au navigateur, et affichée dans son intégralité. L’ergonomie en souffre, sans parler des lenteurs occasionnées. Un bon exemple est le cas des formulaires contenant de nombreuses listes, dépendant les unes des autres.

L’idée est donc de ne modifier que la partie de la page qui le nécessite, sans la renvoyer intégralement au navigateur. Cette opération est impossible depuis le serveur.

Il faut donc décomposer la fonction de modification, en deux parties:

  • Une partie serveur, toujours écrite en PHP, qui sera chargée globalement de fournir l’information nécessaire au changement,
  • Une partie « cliente » (navigateur), écrite en Javascript, qui sera chargée de modifier la page, en fonction des informations reçues.

Le processus devient alors:

  • La page (ou sa structure) est d’abord affichée dans son intégralité, une première fois,
  • En fonction d’un évènement (clic, modification d’un formulaire, …), le script Javascript envoie une requête à la fonction PHP sur le serveur,
  • Celle-ci effectue les opérations demandées, puis renvoie les informations vers le navigateur,
  • Les informations sont interceptées par le script Javascript, qui les utilise pour modifier la page affichée par le navigateur.

La figure ci-dessous tente de résumer le principe décrit:

Principe de fonctionnement de Ajax

Ajax est WordPress

Le principe de fonctionnement de WordPress avec Ajax, est identique à celui que je viens de décrire

  • Génération de la page: Le serveur, via une fonction PHP d’un plugin ou d’un thème, va générer un fichier HTML, puis l’envoyer au navigateur. Cette page générée va contenir également un script Javascript (le client),
  • Ce script va, en fonction de certains évènements, faire appel à la fonction admin-ajax.php, avec un certain nombre de paramètres, dont l’action à réaliser (mon_action)
  • admin-ajax.php va alors lancer l’action demandée, en faisant appel à une fonction PHP qui aura été préalablement attachée au hook wp_ajax_mon_action.
  • Les informations traitées par cette fonction, sont renvoyées au script Javascript, qui va modifier la page affichée.

Application

Passons au concret. Plaçons-nous au niveau de l’interface d’administration de WordPress (back-end). L’objectif de l’exemple est de développer un plugin, qui nous donne le nombre d’articles en fonction d’un status sélectionné.

Le plugin

La structure de départ du plugin sera celle-ci:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
<?php
/*
Plugin Name: My_Ajax_Plugin
Plugin URI:
Description: Test of Ajax in a plugin
Version: 1.0.0
Author: Emmanuel GEORJON
Author URI: http://www.emmanuelgeorjon.com/
*/
if (! class_exists('My_Ajax_Plugin')) {
 
	class My_Ajax_Plugin{
 
		function admin_menu() {
			add_management_page('My Ajax Plugin',
						'My Ajax Plugin tool page',
						'manage_options',
						'myajaxplugin',
						array(& $this, 'my_tool_page'));
		} // End of admin_menu
 
		function my_tool_page() {
?>
			<div class="wrap">
				<?php screen_icon(); ?>
				<h2>My Ajax Plugin tool page</h2>
			</div>
<?php
		} // End of my_tool_page
 
		function load() {
			add_action('admin_menu', array(&$this, 'admin_menu'));
		}// End of load
 
	} /* End of class */
} /* End of class_exists */
 
$my_ajax_plugin = new My_Ajax_Plugin();
$my_ajax_plugin->load();
?>

Explications:

  • Utiliser les objets nous permet d’éviter plus facilement les conflits de nommage avec les autres plugins, sans ajouter trop de complexité,
  • Pour tester Ajax, il nous faut une page. J’ai choisi de la créer dans le menu Tools
  • La fonction admin_menu crée donc le menu. La fonction my_tool_page sera chargée d’afficher la page.

La page du plugin est vide pour l’instant. Nous allons écrire son contenu dès maintenant:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php
function my_tool_page() {
?>
	<div class="wrap">
		<?php screen_icon(); ?>
		<h2>My Ajax Plugin tool page</h2>
		<p>Please select a status of posts:</p>
		<select id="my_ajax_plugin_selection" name="my_ajax_plugin_selection" >
			<option value="none"></option>
			<option value="all">All</option>
			<option value="publish">Published</option>
			<option value="future">Planned</option>
			<option value="pending">Pending</option>
			<option value="draft">Draft</option>
		</select>
		<p id="message"> </p>
	</div>
<?php
} // End of my_tool_page
?>

Deux éléments HTML: d’abord un champ select pour choisir le status des articles, et une zone délimitée par la balise p, qui servira de zone d’affichage.

Ajout des mécanismes Ajax

Jusqu’à présent tout est assez classique. Nous allons maintenant développer la partie « AJAX » du plugin. Il faut pour cela:

  1. Ecrire le script Javascript,
  2. Charger ce script dans la page,
  3. Ecrire la fonction PHP qui va renvoyer le nombre d’articles,
  4. Rattacher cette fonction au bon hook.

Commençons par le script Javascript:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
jQuery(document).ready(function($) {
 
	jQuery('#my_ajax_plugin_selection').change( function() {
 
		jQuery.ajax({
			type: 'POST',
			url: MyAjaxPlugin.ajaxurl,
			dataType: 'json',
			data: {
				action : MyAjaxPlugin.action,
				myajaxpluginnonce : MyAjaxPlugin.nonce,
				selectval :  jQuery("select option:selected").val()
			},
			success : function(data){
				if (data.error == 0) {
					jQuery("div#ajax-response").removeClass().addClass('updated').show().html('Operation done successfully');
					jQuery("p#message").html( data.msg );
				}
				else {
					jQuery("div#ajax-response").removeClass().addClass('error').show().html(data.msg);
				}
			},
			error : function(XMLHttpRequest, textStatus, errorThrown) {
				jQuery("div#ajax-response").removeClass().addClass('error').html('Cannot execute the request properly!');
			} // End of success function
		});
	});
});

Ce script est sauvegardé dans le fichier my_ajax_plugin.js.

Explications:

  • En ligne 3, nous déclenchons l’action dès qu’une valeur, différente de la précédente, est sélectionnée,
  • La variable data contiendra tous les paramètres reçus par la fonction PHP. Le champ action prend la valeur my_ajax_plugin_test, et le champ selectval va prendre la valeur sélectionnée (nous allons voir comment juste après).
  • La ligne 10 lance l’appel à la fonction PHP. La variable ajaxurl est définie automatiquement dans WordPress, et vaut .../wp-admin/admin-ajax.php.
  • Dans la ligne 10, nous lançons donc admin-ajax.php, avec les paramètres action et selectval, la réponse envoyée par la fonction PHP, sera récupérée, puis affichée.

Il nous faut modifier notre classe initiale pour charger ce script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<?php
/*
Plugin Name: My_Ajax_Plugin
Plugin URI:
Description:
Version: 1.0.0
Author:
Author URI:
*/
if (! class_exists('My_Ajax_Plugin')) {
 
	class My_Ajax_Plugin{
 
		function admin_menu() {
			$hook = add_management_page('My Ajax Plugin',
					'My Ajax Plugin tool page',
					'manage_options',
					'myajaxplugin',
					array(& $this, 'my_tool_page'));
 
			add_action('admin_print_scripts-'.$hook, array(&$this, 'load_scripts'));
		} // End of admin_menu
 
		function my_tool_page() {
 
			if ( ! current_user_can( 'manage_options' ) )
				die('You cannot use this page');
?>
			<div class="wrap">
				<?php screen_icon(); ?>
				<h2>My Ajax Plugin tool page</h2>
				<div id="ajax-response" >&nbsp;</div>
				<p>Please select a status of posts:</p>
				<select id="my_ajax_plugin_selection" name="my_ajax_plugin_selection" >
					<option value="none"></option>
					<option value="all">All</option>
					<option value="publish">Published</option>
					<option value="future">Planned</option>
					<option value="pending">Pending</option>
					<option value="draft">Draft</option>
				</select>
				<p id="message"> </p>
			</div>
<?php
		} // End of my_tool_page
 
		function load_scripts() {
 
			wp_enqueue_script('my_ajax_plugin_js_script', plugin_dir_url(__FILE__).'/my_ajax_plugin.js', array('jquery'), TRUE);
			wp_localize_script('my_ajax_plugin_js_script', 'MyAjaxPlugin', array(
				'ajaxurl'  => admin_url('admin-ajax.php'),
				'action'	=> 'my_ajax_plugin_test',
				'nonce'		=> wp_create_nonce('my_ajax_plugin_nonce')
				)
			);
		} // End of load_scripts
 
		function load() {
			add_action('admin_menu', array(&$this, 'admin_menu'));
		}// End of load
 
	} /* End of class */
} /* End of class_exists */
 
$my_ajax_plugin = new My_Ajax_Plugin();
$my_ajax_plugin->load();
 
?>

Deux modifications principales:

  • Dans la fonction , nous ajoutons la ligne add_action('admin_print_scripts-'.$hook pour désigner la fonction qui effectuera le chargement du script,
  • Ajout de la fonction load_script
    • Nous laissons à WordPress le soin de gérer proprement le chargement du script, grâce à l’utilisation de la fonction wp_enqueue_script. Nous indiquons que jQuery est un prérequis, et que le script doit être chargé en fin de page,
    • Nous détournons ensuite la fonction wp_localize_script de son rôle initial: normalement cette fonction permet de « traduire » des chaînes de caractères situées dans les scripts Javascript. Ici nous nous en servons pour « coder » certains paramètres utilisés par le script JS. De cette façon, si l’un de ces paramètres change, nous n’avons que la fonction load_script à modifier, sans rien toucher au reste. La variable ajaxurl utilisée plus haut est codée de cette façon.

A ce stade, qu’avons-nous?

  • Une page accessible depuis le menu Tools,
  • Un script JS chargé lors de l’affichage de cette page,
  • Ce script JS va lancé admin-ajax.php, avec un certain nombre de paramètres, dont action=my_ajax_plugin_test

Il ne nous reste donc plus qu’a

  • Développer la fonction PHP capable de répondre à cette requête,
  • Indiquer à la fonction admin-ajax.php les coordonnées de cette nouvelle fonction.

Commençons par la fonction PHP:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<?php
function test() {
	$error_code = 0;
	$msg = '';
 
	if (! check_ajax_referer('my_ajax_plugin_nonce','myajaxpluginnonce', FALSE)) {
		$error_code = 1;
		$msg        = 'Security error';
	}
	else if ( ! current_user_can( 'manage_options' ) ) {
		$error_code = 2;
		$msg		= 'Access denied';
	}
	else {
		$num_posts = (array)wp_count_posts('post');
 
		if (isset($_POST['selectval'])) {
			$status = $_POST['selectval'];
			switch ($status) {
				case 'none':
					$msg = '';
				break;
 
				case 'all':
					$msg = 'Total number of posts:'.array_sum($num_posts);
				break;
 
				default:
					if (isset($num_posts[$status]))
						$msg = 'Number of posts with status <strong>'.$status.'</strong> is <strong>'.$num_posts[$status].'</strong>';
					else
						$msg = 'Unknow status';
				break;
			}
		}
	} // End of current_user_can
 
	echo json_encode(array('error' => $error_code, 'msg' => $msg));
	exit;
} // End of test
?>

Explications:

  • Nous commençons par vérifier la partie sécurité, avec un check_ajax_referer pour s’assurer que la requête vient bien du site, puis avec un current_user_can pour s’assurer qu’un utilisateur est connecté et qu’il a les bons droits,
  • La fonction qui nous permet de récupérer en une seule fois, le nombre d’articles par status est wp_count_posts, qui renvoie un objet, convertible en tableau, ce que nous faisons pour des raisons pratiques,
  • Nous récupérons le status sélectionné par l’utilisateur, dans $_POST['selectval']
  • La fonction exit à la fin de la fonction est très importante: Javascript interprête le code de retour de la fonction. Si celui-ci n’est pas égal à 0, il considérera que la fonction ou l’appel à la fonction ont échoué. exit est équivalent à exit(0),
  • A noter que nous utilisons la fonction json_encode qui n’est disponible qu’à partir de PHP 5.3.

Il ne reste plus qu’à rattacher cette fonction, au bon hook:

<?php
	add_action('wp_ajax_my_ajax_plugin_test', array(&$this, 'test') );
?>

Le second paramètre correspond au nom de la fonction définie juste avant, tandis que le premier paramètre prend la forme wp_ajax_action, ou action doit avoir la même valeur que le paramètre dans le script Javascript.

Testons le résultat:

 

Vous pouvez télécharger le source complet du plugin ICI:


Sources du plugin
Titre: Sources du plugin (329 clics)
Légende:
Nom du fichier: my_ajax_plugin.zip
Taille: 2 KB

Partie publique du blog

Toutes les explications données jusqu’à présent concernent l’interface d’administration d’un blog.
Que se passe-t-il si l’on veut également utiliser Ajax pour la partie publique?

Globalement le fonction reste le même, seul le hook change: il ne faut plus utiliser wp_ajax_mon_action, mais wp_ajax_nopriv_mon_action

Si vous souhaitez utiliser une même action, à la fois du côté privé, et du côté publique du blog, il faudra mettre en place un code du type:

<?php
 
if ( is_admin() ) { // Partie du code pour la partie privée
 
	// La fonction load_script est chargée lorsque la page du plugin s'affiche
 
    add_action('wp_ajax_my_ajax_plugin_test', array(&$this, 'test') );
    add_action('wp_ajax_nopriv_my_ajax_plugin_test', array(&$this, 'test') );
 
}
else { // Partie du code pour la partie public
	add_action('wp_print_scripts-'.$hook, array(&$this, 'load_scripts'));
}
?>

Le script Javascript va appeler la même fonction admin_url('admin-ajax.php'). Mais contrairement à la zone d’administration, la variable ajaxurl n’est pas définie côté publique. Sa déclaration n’est plus facultative, mais obligatoire.

Même si la fonction Ajax est destinée à la partie publique du blog, le hook doit être déclaré du côté privé (interface d’administration).

Conclusion

Ajax apporte un confort d’utilisation indéniable aux utilisateurs. Côté développeur, la complexité supplémentaire n’est pas si importante que cela, même si le débuggage est plus délicat: les erreurs de Javascript sont rarement faciles à trouver (absence de message d’erreur), et la partie PHP, lancée par le Javascript, est muette, même en cas d’erreur. L’idéal est donc de faire fonctionner le plugin, d’abord en mode « classique », avant de le convertir en mode Ajax.

Référence

1 thought on “WordPress et Ajax”

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.