Depuis Drupal 5, il est possible très simplement d'ajouter l'auto-complétion à un champ texte en utilisant une méthode dit d'appel asynchrone (aka Ajax). Traduit en français, cette fonctionnalité permet lorsque vos utilisateurs saisissent les premières lettre, de lui fournir automatique une liste de suggestion commençant par ces lettres, lui évitant de taper la suite.
L'auto-complétion (ou suggestion) est donc un appel asynchrone. C'est à dire que lorsque l'utilisateur saisi une lettre dans le champ texte, au bout d'un temps court, une procédure JavaScript va lancer une requête prenant en paramètre le texte qui a été tapé. Le serveur (Drupal) va recevoir cette URL, et exécuter la procédure associée qui a pour tache de construire une liste de suggestion. Ceci fait le serveur renvoi à la procédure javascript le résultat qui va l'utiliser pour fabriquer, à la volée, une petite liste déroulant pour l'utilisateur.
Dans l'exemple qui suit, le champ que nous allons modifier, permet d'obtenir une liste de termes (taxonomie), suggérés à partir de ce que l'utilisateur a saisir, et ceci pour un vocabulaire donné. Il peut être adapté à d'autre exemples facilement.
La première chose que nous avons à faire est donc de déclarer dans Drupal un chemin (path) qui va rediriger cette URL vers une procédure capable de lister les bonnes réponses. Et qui dit path, dit dit un hook _menu :
Etape suivante, écrire la procédure déclarée dans le hook _menu par le paramètre callback et qui va générer notre liste de suggestions.
Quelques explications s'imposent. Tout d'abord le paramètre $string. Ce dernier contient, vous l'aurez deviné, la chaîne à rechercher. Cela correspond à tout ce qui se trouve après le mon_module/categories/autocomplete dans l'url. Cela marche très bien sauf dans un cas, si vous voulez chercher des choses qui contiennent des /. Là ça marche beaucoup moins bien car $string ne va contenir que ce qui se trouve avant le premier / (logique). Du coup, il faut tricher un peu et rajouter une horreur de ce genre au début de la procédure :
Cela va prendre à la source ($_GET['q']) la totalité de l'URL drupal et enlever la partie qui ne sert à rien (path).
Une fois la variable $string en main, il suffit de faire une requête SQL pour récupérer une liste limitée à 10 items de réponse qui concordent avec notre chaîne. Notez que dans la requête d'exemple, la recherche se fait sur le vocabulaire ayant comme vid la valeur 10. Pensez à changer cela pour utiliser votre propre vid.
Une fois la liste constituée ($matches), on utilise la fonction magique de drupal drupal_to_js qui va créer une petite procédure javascript contenant nos données, utilisable par l'auto-complétion de Drupal.
Avant d'aller plus loin, nous pouvons déjà tester si tout les suggestions fonctionnent, et dans FireFox, rentrer une URL artificiel du style http://localhost/mon_drupal?q=mon_module/categories/autocomplete/a. Une fois exécutée, la page affichée doit contenir tout les termes qui commencent par la lettre a.
Maintenant que l'on sait que notre procédure fonctionne, il ne nous reste plus qu'à ajouter dans un formulaire (soit un mon_module_form_alter, soit un mon_module_form), un champ texte tout con :
Rien de bien extraordinaire mis à part le champ '#autocomplete_path' qui est seul responsable de la magie. Il suffit d'afficher votre formulaire pour voir apparaître votre champ texte doté d'un étrange petit cercle gros sur la droite. Ce symbole indique que l'auto-complétion est disponible sur ce champ. Tapez une lettre (a) et attendez une seconde, une liste devrait alors apparaître avec vos termes de vocabulaire.
Voilà comment en peu de ligne vous pouvez changer la vie de vos utilisateurs, leur évitant les anti-ergonomiques liste déroulantes contenant des centaines d'item.
- répondre
tenshu , le 15 November, 2007 - 12:47y'a t'il une façon plus simple d'utiliser ajax via jquery et drupal_add_js() ?
- répondre
Ulhume, le 15 November, 2007 - 13:04Quel veux-tu dire par "plus simple" ? Pour faire quoi exactement ?
- répondre
tenshu , le 17 November, 2007 - 19:50bien si je me goure pas drupal 5 intègre jquery dans son core?
Jquery propose l'auto complétion
Jquery s'utilise avec drupal_add_js()
Donc n'y a t'il pas moyen de faire plus simple?
Je m'explique mieux?
- répondre
Ulhume, le 17 November, 2007 - 20:07Ben non, pas à ma connaissance du moins. Le code qui est ici sert surtout à mettre en place le champ, puis la source de données correspondant à l'auto-complétion de ce champ (interrogation de base + drupal_add_js()). Donc si tu pars d'un besoin nouveau (comme ici), il ne peut y avoir moins de code.
En revanche, si tu veux faire l'auto-complétion sur un champ utilisateur par exemple, il y a juste la bonne call-back à indiquer dans la définition du champ et rien d'autre. C'est ce que tu veux dire par plus simple ? Car sinon, je vois mal comment faire plus simple que ce qui est exposé ici, en tout cas avec autant de souplesse.
- répondre
tenshu , le 17 November, 2007 - 20:35ok je débute avec drupal
- répondre
Ulhume, le 18 November, 2007 - 00:07Pas de soucis
n'hésites pas en tout cas !
- répondre
gagarine , le 23 November, 2007 - 11:32l'auto-completion c'est cool mais je trouve qu'il faudrait pouvoir afficher plus d'info.
.
Par exemple si je des node de ville, Fribourg par exemple
Je tape Freibourg et drupal vas me présenter deux résultats:
-Freibourg
-Freibourg
Un est Freibourg CH et l'autre Freibourg DE Il faudrait donc pouvoir aller rechercher plus d'info sur ma ville pour avoir quelque chose comme ça:
-Freibourg CH
-Freibourg DE
et une question en plus...
si on a énormément de node comme faire pour placer une sorte de recherche qui vas pres séléctionner les nodes?
- répondre
Ulhume, le 23 November, 2007 - 14:30Dans la requête SQL donnée en exemple, je ne sélectionne que le champ "name", rien ne t'empêches cependant de faire une sélection plus large, remontant plus de champs, ou même de faire une jointure pour faire une recherche sur plusieurs tables. Par exemple avec un node de type "ville" et une table associée "extension" qui contient l'information de pays, cela donnerait :
SELECT n.title, e.pays
FROM node n
INNER JOIN e extention ON e.nid=n.nid
WHERE LOWER(e.title) LIKE LOWER('%s%%')", $string, 0, 10);
while ($term = db_fetch_object($cursor))
$matches[$term->name] = $term->title.' '.$term->pays;
Et ce type de requête, pourvu que tu ais des indexes, ne pose pas de problème même sur un grand nombre de node, le paramètre (10) fait que tu ne remontes que 10 entrées (en fait Drupal modifie la requête pour ajouter un simple LIMIT 10 à la fin). Pas besoin donc de préselectionner quoi que ce soit, si j'ai bien compris ta question.
- répondre
Anonymous , le 23 November, 2007 - 18:22Ok merci très intéressant... tu réponds parfaitement à ma première question
La deuxième question n'est pas tout a fait en rapport avec le post.
En faite ce que j'aimerai faire c'est par exemple avoir deux champs de recherches liée. Pour continuer dans la même voix:
[payse] [ville]
Le deuxième étant grisé tant que je n'ai pas sélectionné le 1er. Je choisie donc d'abord le pays et dans le deuxième champs je n'ai plus que des propositions de villes du pays en question.
En gros permettre à l'utilisateur d'envoyer des paramètre de recherches avant de choisir le node pour limiter les résultats.
Ca peut être très utile si je sais que je veux sélectionner une ville Suisse me ne me rappelle plus son nom (évidement dans ce cas le deuxième champs doit être une simple liste déroulant).
J'espère avoir été plus clair
Poster un nouveau commentaire