Afficher des résultats instantanés en utilisant jQuery, XML et PHP

Fatigué des interfaces Web ringardes et des rechargements de page qui s’éternisent ? De quoi parlons-nous ? De l’AJAXAsynchronous Javascript And XML. Vous allez ainsi pouvoir développer de superbes applications, plus interactives, plus réactives et plus faciles à utiliser. Dans cet article nous allons construire une page affichant des résultats « instantanés » avec une combinaison de jQuery, XML et PHP.

Débuter avec l’instantané

Vous connaissez certainement la fonction de recherche instantanée de Google, qui dès lors que vous effectuez une nouvelle recherche, vous montre les résultats que vous tapez, en fonction de sa popularité de recherche. Tout ce que vous avez à faire afin d’obtenir des résultats est juste taper quelques lettres. Vous n’avez pas besoin d’appuyer sur la touche « Entrée » pour voir les résultats, il vous suffit de modifier votre recherche et appuyez de nouveau sur « Entrée ». C’est incroyable de voir comment un tel petit changement peut faire une énorme différence en matière de convivialité.

La bonne nouvelle c’est que ce type de fonctionnalité instantanée, est facile à mettre en œuvre, surtout lorsque vous utilisez du côté client des outils tels que jQuery. Dans cet article, nous allons suivre le processus de construction d’un moteur de recherche simple et ensuite construire une interface utilisateur de recherche instantanée pour ce moteur.

Tout commence par l’obtention des données de recherche.

Mise en place des données

Pour cet article, j’ai décidé de chercher des livres sur l’informatique. J’ai mis en place un fichier XML qui contient tous les livres dédiés à l’informatique que je possède avec leurs titres, l’auteur, la date de publication, et un rapide résumé du livre. Vous pouvez voir une partie de l’XML dans le listing ci-dessous :

[sourcecode language="XML"]
<?xml version="1.0" encoding="UTF-8"?>
<books>
<book title=’Stratégie de contenu web’ author=’Erin Kissane’ publication=’juillet 2011′ edition=’Eyrolles’>
La stratégie de contenu est la nouvelle tendance du Web. Mais d’où vient-elle ? Pourquoi est-elle si importante ? Et que signifie pour vous ce grand retour du contenu ? Erin Kissane explore dans ce guide les origines de la stratégie de contenu et montre, avec concision et expertise, comment mener à bien cette discipline complexe.
</book>

</books>
[/sourcecode]

C’est en fait un fichier qui compte environ 1500 lignes. Effectivement j’en ai quelques-uns…

La prochaine étape est d’écrire une classe PHP qui va réaliser le parsing du fichier XML et effectuer les recherches pour vous. Cette classe est appelée Book :

[sourcecode language="PHP"]
<?php
class Book {
private $books = array();
public function __construct() {
$xmlDoc = new DOMDocument();
$xmlDoc->load("books.xml");
foreach ($xmlDoc->documentElement->childNodes as $book) {
if ($book->nodeType == 1 ) {
$this->books[] = array(
‘book’ => $book->getAttribute(‘book’),
‘title’ => $book->getAttribute(‘title’),
‘author’ => $book->getAttribute(‘author’),
‘publication’ => $book->getAttribute(‘publication’),
‘edition’ => $book->getAttribute(‘edition’),
‘summary’ => $book->nodeValue);
}
}
}
public function find($query) {
$found = array();
$re = "/".$query."/i";
foreach( $this->books as $book) {
if (preg_match($re, $book['summary']) ||
preg_match($re, $book['title'])) {
$found []= $book;
}
}
return $found;
}
}
?>
[/sourcecode]

Le constructeur de la classe lit le fichier XML en utilisant la librairie DOM XML qui est standard à PHP. Il parcourt tous les enfants du nœud racine et extrait le titre, l’auteur, la date de publication, les attributs du livre et le texte du nœud qui contient le résumé. Il ajoute ensuite toutes ces données dans un tableau (array()) afin de constituer un tableau de livres, qui est une variable membre de la classe.

La fonction find() recherche alors dans la liste des livres afin de trouver des correspondances en utilisant une correspondance par une simple expression régulière sur le titre et le résumé. Toutes les correspondances sont ajoutées à un tableau, qui est ensuite retournée à l’appelant. Si le tableau est vide, cela signifie qu’aucun résultat n’a été trouvé.

Avec les données en main, la prochaine étape va être de commencer à construire l’interface instantanée avec l’appel à AJAX afin de récupérer les données.

Créer la page de réponse AJAX

La première version de l’interface utilisateur utilise une réponse HTML pour la requête AJAX. Cette approche est le moyen le plus facile pour mettre en œuvre une interface instantanée. La page Web prend le terme de recherche et fait une requête AJAX au serveur en utilisant ce terme. Au lieu de retourner une page HTML entièrement nouvelle, puisque l’on est en train d’écrire une application AJAX, le serveur n’a pas besoin de renvoyer tout ce HTML dans la réponse. Afin de mettre à jour la page Web, il suffit que le serveur transmettre les données brutes, en l’occurrence le nombre de livres correspondants à ma recherche. Ainsi le code reçu remplace alors une partie de la page avec le code HTML mis à jour et ce, en un seul appel.

Plus tard dans l’article, je vous démontrerais comment utiliser une réponse XML ou une réponse JSON venant du serveur, mais pour le moment, juste pour garder les choses simples, nous allons commencer par la version HTML.

La première chose dont nous avons besoin c’est de créer la page de réponse HTML. Celle-ci a besoin nécessairement d’une chaîne de caractère provenant de la demande. Puis on utilise cette chaîne pour appeler la classe Book afin de rechercher les livres. Enfin le tableau de livres construit est retourné au format HTML. Je vous propose de regarder ce code :

[sourcecode language="PHP"]
<?php
include ‘book.php’;

if (!isset($_REQUEST['q'])) {
?>
Aucune requête de formulée
<?php
return;
}

$book = new Book();
$books = $book->find($_REQUEST['q']);
if (count($books) == 0) {
?>
Aucun résultat trouvé
<?php
} else {
?>
<table>
<?php foreach($books as $book) { ?>
<tr>
<td class="book">
<strong><?php echo( $book['title'] ) ?></strong> –
Auteur <?php echo($book['author']) ?>
Publication <?php echo($book['publication']) ?> –
Edition <?php echo($book['edition']) ?>
</td>
</tr>
<tr>
<td class="summary">
<?php echo( $book['summary']) ?>
</td>
</tr>
<?php } ?>
</table>
<?php
}
?>
[/sourcecode]

En haut de ce listing, est incluse la classe Book. Le code crée ensuite une nouvelle instance de celle-ci et fait appel à la méthode find(). On regarde ensuite pour voir si la réponse de l’appel à la fonction est vide, et si elle retourne « Aucun résultat trouvé », sinon, on parcourt les résultats et on créé un tableau de résultats.

Pour tester la page, il suffit d’aller sur votre navigateur Web et appeler cette page. Vous pouvez voir le résultat ci-dessous :

Afficher des résultats instantanés en utilisant jQuery, XML et PHP - Résultat d'une recherche depuis le navigateur

À ce stade, vous avez tout ce qu’il faut pour commencer à construire l’interface utilisateur de recherche instantanée.

Création de l’interface pour la recherche instantanée

L’utilisation de la bibliothèque JavaScript, jQuery, permet de construire l’interface utilisateur de recherche instantanée très facilement. Regardez le listing ci-dessous, pour vous en convaincre :

[sourcecode language="HTML"]
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Résultat Instantané – HTML</title>
</head>
<body>
Recherche livres : <input type="text" id="term" />
<div id="results"> </div>
<script>
$(document).ready(function() {
$(‘#term’).keyup(function() {
$.get(‘ajax_html_response.php?q=’+escape($(‘#term’).val()), function(data) {
$(‘#results’).html(data);
});
});
});
</script>
</body>
</html>
[/sourcecode]

L’inclusion de la bibliothèque jQuery et d’une feuille de style CSS est faite en haut de ce listing. Le corps de la page comporte un champ de saisie (input) pour le terme de recherche et une div qui contient la sortie.

Le gros du travail se fait dans la section JavaScript au bas de la page. On commence par un appel à la fonction ready() sur le document. Cet appel s’assure que le JavaScript n’est pas exécuté jusqu’à ce que la page soit totalement chargée. Ensuite on utilise la fonction keyup() sur l’objet de la recherche pour surveiller les touches qui sont pressées sur le champ texte (input) de la recherche. Lorsque ce dernier est modifié, la méthode AJAX get() est appelée sur le serveur. On récupère les données de la réponse de cet appel, qui sont utilisées pour remplir l’élément résultats (results) en utilisant la fonction html().

Vous pouvez faire tout ce travail sans la bibliothèque jQuery, mais l’utilisation de cette bibliothèque permet d’avoir un code concis et tout le travail multi-plateforme a été fait pour vous. Vous n’avez pas à vous soucier des différences entre Internet Explorer, Safari, Chrome ou encore Firefox, il vous suffit d’écrire le code une fois et ça fonctionne partout.

Pour tester la bibliothèque, il vous suffit de faire apparaître l’interface de recherche instantanée dans un navigateur Web.

Afficher des résultats instantanés en utilisant jQuery, XML et PHP - Quelques lettres tapées dans le champ de recherche

L’image ci-dessus montre l’interface après que j’aie entré quelques caractères. Après je complète ma recherche en tapant le terme « PHP ». Vous voyez les résultats ici :

Afficher des résultats instantanés en utilisant jQuery, XML et PHP - Après avoir complété la recherche

L’image ci-dessus montre que le terme « PHP » apparait dans le titre ou dans le résumé de quatre livres. Le temps de réponse sur ma machine locale était excellent, même malgré l’analyse côté serveur d’un fichier XML de plus de 900K.

Maintenant, vous voudriez peut-être mettre un délai entre chaque frappe et afficher uniquement les résultats quand vous en ferez la demande ? Voici le code mis à jour :

[sourcecode language="HTML"]
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Résultat Instantané – HTML</title>
</head>
<body>
Recherche livres : <input type="text" id="term" />
<div id="results"> </div>
<script>
delayTimer = null;

function getResults() {
$.get(‘ajax_html_response.php?q=’+escape($(‘#term’).val()), function(data) {
$(‘#results’).html(data);
} );
delayTimer = null;
}

$(document).ready(function() {
$(‘#term’).keyup(function() {
if ( delayTimer )
window.clearTimeout( delayTimer );
delayTimer = window.setTimeout( getResults, 200 );
});
});
</script>
</body>
</html>
[/sourcecode]

Le code déclenche un timer lorsque l’utilisateur appuie sur une touche. Lorsque cette temporisation arrive à son terme, au bout de 200 millisecondes, la demande est faite au serveur. Si une autre touche est entrée avant que le timeout se soit terminé, ce dernier est annulé et un nouveau timer est créé. Le résultat est que le timer se déclenche de 200 millisecondes après que l’utilisateur ait cessé de frapper. L’interface possède toujours les mêmes caractéristiques que précédemment, mais le nombre de demandes faites au serveur est considérablement réduite, en particulier lorsque les utilisateurs tapent rapidement au clavier.

On pourrait s’arrêter là, mais il y a deux autres façons permettant de réaliser ce processus instantané.

Migration vers XML

La première façon est d’utiliser XML comme syntaxe de transport à partir du serveur vers le client. L’idée ici est que le serveur fournit un retour générique en XML permettant à tout processus de l’utiliser pour effectuer des requêtes. Bien entendu, le client développé devra être assez intelligent pour lire le format XML.

Pour passer au format XML, créez d’abord une nouvelle page sur le serveur comme ci-dessous :

[sourcecode language="PHP"]
include ‘book.php’;

header(‘Content-type: text/xml’);

$book = new Book();
$doc = new DOMDocument();
$root = $doc->createElement(‘books’);
$doc->appendChild($root);
foreach($book->find( $_REQUEST['q'] ) as $book) {
$el = $doc->createElement( ‘episode’ );
$el->setAttribute(‘title’, $book['title']);
$el->setAttribute(‘author’, $book['author']);
$el->setAttribute(‘publication’, $book['publication']);
$el->setAttribute(‘edition’, $book['edition']);

$tn = $doc->createTextNode( $book['summary']);
$el->appendChild($tn);

$root->appendChild($el);
}
print $doc->saveXML();
[/sourcecode]

La recherche s’effectue de la même manière que précédemment, seul le format de retour des résultats est différent. Maintenant, le code crée un document XML et des nœuds sont ajoutés afin de contenir toutes les données. À la fin du script, il enregistre simplement le DOM XML en tant que chaîne. Notez que dans le haut du script vous définissez également le type du contenu de la sortie, text/xml, pour signifier que vous allez exporter du XML et non de l’HTML.

Si vous allez sur cette page depuis votre navigateur Web, vous devriez avoir un affichage équivalent à ceci :

Afficher des résultats instantanés en utilisant jQuery, XML et PHP - La page de réponse XML

Certains navigateurs, cependant, peuvent afficher de manière plus structurée le contenu. Si vous voulez voir la source originale XML, vous pouvez choisir d’afficher la source à l’aide du clic droit sur la page Web et vous devriez voir quelque chose comme la fenêtre ci-dessous :

Afficher des résultats instantanés en utilisant jQuery, XML et PHP - La source XML de la page

Le nouveau code côté client qui analyse les données au format XML plutôt que l’HTML :

[sourcecode language="HTML"]
<html>
<head>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.5.0/jquery.min.js"></script>
<link rel="stylesheet" href="styles.css" type="text/css" />
<title>Résultat Instantané – XML</title>
</head>
<body>
Recherche livres : <input type="text" id="term" />
<div id="results"> </div>
<script>
$(document).ready(function() {
$(‘#term’).keyup(function() {
$.get(‘ajax_xml_response.php?q=’+escape($(‘#term’).val()), function(data) {
html = ‘<table id="results">’;
$(data).find(‘episode’).each( function() {
var ep = $(this);
html += ‘<tr><td class="book"><b>’+
ep.attr(‘title’)+’</b>&nbsp;’;
html += ‘Auteur ‘+ep.attr(‘author’)+’&nbsp;’;
html += ‘Publication ‘+ep.attr(‘publication’)+’&nbsp;’;
html += ‘Edition ‘+ep.attr(‘edition’)+’</td></tr>’;
html += ‘<tr><td class="summary">’+
ep.text()+’</td></tr>’;
});
html += ‘</html>’;
$(‘#results’).replaceWith(html);
});
});
});
</script>
</body>
</html>
[/sourcecode]

Le code client surveillant les caractères entrés au clavier et faisant la requête AJAX est quasiment identique. La seule différence est le changement d’URL. Une fois les données récupérées, le code utilise jQuery pour trouver toutes les balises book. On formate les données XML récupérées en une chaîne de caractères, et on utilise la fonction replaceWith() afin de mettre à jour le contenu. Avec l’aide de jQuery, ce code est facile à utiliser a contrario si on avait utilisé les fonctions natives DOM du navigateur.

Il existe une autre façon de transférer les données, à l’aide de JSON (JavaScript Object Notation).

Migration vers JSON

JSON est un moyen très populaire pour transférer des données dans le monde du Web 2.0. Il est compact et rapidement lisible pour le navigateur parce que tout ce qu’il a à faire c’est évaluer le code JavaScript retourné. Il est également très facile de créer un objet JSON comme vous pouvez le voir :

[sourcecode language="PHP"]
<?php
include ‘book.php’;

header(‘Content-type: text/plain’);

$book = new Book();
print json_encode($book->find($_REQUEST['q']));
?>
[/sourcecode]

Vous avez seulement besoin d’utiliser la fonction json_encode() pour retourner le tableau sous le format JSON. La plupart des langages populaires ont des mécanismes de traitement afin de transformer des données JSON.

Si vous ouvrez la page créée dans votre navigateur, vous devriez voir quelque chose comme ceci :

Afficher des résultats instantanés en utilisant jQuery, XML et PHP - La page de réponse JSON

Il ne vous reste plus qu’à modifier le code client afin de lire les données JSON.

Des choses à savoir…

Cette application a trois principales différences avec le mécanisme proposé par les ingénieurs de Google. La première est l’échelle. En effet, ils manipulaient déjà des milliards de recherches chaque jour, maintenant ils manipulent des milliards de recherches individuelles à chaque frappe. Il y a beaucoup de questions et de solutions autour de cela, mais la principale solution reste le cache navigateur. Si l’utilisateur tape le même terme de recherche à deux reprises, grâce à la mémoire cache du navigateur, une seule demande au serveur est faite, puisque la deuxième requête est directement renvoyée par les données en cache du navigateur.

La deuxième différence est que Google va pré-rechercher des résultats. Autrement dit, si vous tapez « ph » il va proposer des mots comme « php », en indiquant avec un texte gris le « p » qui vous manque.

La troisième différence est le support pour la pagination, qui est assez facile à résoudre. Tout ce que vous avez à faire est d’ajouter un peu de JavaScript et des liens au bas de la page et ensuite appeler ce script lorsque l’utilisateur clique pour naviguer sur les différentes pages.

Démos et téléchargement

Vous pouvez télécharger la démo de l'article dans un seul fichier ZIP.

Conclusion

La fonction de recherche de Google est vraiment instantanée. Est-elle révolutionnaire ? Pas vraiment. Mais c’est un petit pas qui a des implications profondes pour la convivialité. Comme vous pouvez le voir sur cet article, cette technique n’est pas difficile à mettre en œuvre, et ce en utilisant des outils standards comme XML, PHP et jQuery.

J’espère que vous serez en mesure d’utiliser le code présenté ici dans vos propres projets.

N’hésitez pas à laisser vos commentaires ainsi que vos projets pour lesquels vous utilisez cette technique.

  • http://www.baverty.com Benoit

    Personnellement je ne suis pas fan de la fonction de recherche instantanée de Google. De manière générale, je trouve que c’est abuser de Javascript que de déclencher des actions à des moments autres que ceux prévus dans le code HTML. C’est troublant de voir la page entière changer alors qu’on en a pas vraiment besoin.

    Ceci dit, utiliser AJAX est effectivement un gros plus, et ne pas recharger la page entière au clic sur le bouton de recherche est non seulement plus joli et moderne, mais c’est aussi un peu plus performant.

    Très bonne introduction à l’AJAX ceci dit. C’est vrai que l’apparition des frameworks Javascript tels que jQuery ont beaucoup simplifié son usage, et plus personne n’a d’excuse pour ne pas l’utiliser

    • Anonyme

      Il est vrai que cela peut sembler déroutant … mais il s’avère que c’est une technique très intéressante notamment pour éviter de surcharger le serveur !

      L’idée de cet article est vraiment de comprendre la notion de l’AJAX avec un exemple connu par tout le monde ;)

      • http://www.baverty.com Benoit

        Justement, pour ne pas surcharger je trouve que la requête à chaque modification (même avec un timer) n’est pas du tout optimale. Au clic du bouton je suis tout à fait d’accord, XML et JSON étant beaucoup plus légers que du HTML.

        Quand à l’article, je suis d’accord, c’est une bonne idée d’utiliser un exemple connu et je ne critique pas ça.

        • http://www.cv-developpeur-web2.com Yohann Poiron

          Je comprends ce que tu veux dire ;) et je partager ton avis ! Par contre proposer la fin de résultat de recherche et en précharger le contenu du résultat c’est tout de même très pratique…

  • http://www.arnaud-olivier.fr Tutorial webmaster

    En tout cas le résultat final est vraiment bien, je retiens surtout le côté pratique niveau utilisateur c’est un plus je pense !

  • Seb

    Tres bon article. Je rajouterai juste pour une bonne interpretation du XML de specifier le datatype XML et JSON avec $.get (http://jquery.developpeur-web2.com/documentation/ajax/$.get.php).

    • http://www.cv-developpeur-web2.com Yohann Poiron

      Bien vu Seb ;)

  • http://alineowebdesign.com/ Jonathan – alineowebdesign

    Super article, je met ça bien au chaud dans mes docs :)

  • Lali-laliha

    bonjour Svpp aider moi, en faite notre partenaire nous a donné adresse de flux xml a recupirer pour la demande de disponibilité au meme temps pour la reservation , donc je dois envoyer des demandes en get ou en post???, pour recupirer juste partie de fichier xml qui se trouve a l’adresse, comment peut t-on faire ça, en plus c’est quoi demande en get ou en post???merci

  • Omar

    Article très pratique Merci à l’auteur. sinon j’y trouve une petite defaut qui est le fait que si l’utilisateur vide le chanp de recherche après une premiere recherche, le moteur affiche tout les livres disponible dans le catalogue. donc une amelioration qui me semble pratique c’est de faire en sorte que le scripte revien a l’etat initial cet a dire ne rien afficher. est ce quelqu’un en possede une solution ? Merci d’avance.