Enfin un tutoriel ! Cela faisait bien longtemps que je n’en avais pas publié (par faute de temps …) !
Sujet du jour : sfWidgetFormDoctrineJQueryAutocomplete
Ce widget propose un champ input de type text qui, lorsque vous tapez des données, effectue une recherche en ajax dans la base de données (ici, nous travaillons avec Doctrine) et dans le cas de résultats, qui vous les retranscrit dans une liste placée sous le champ input.
L’idée étant de rechercher rapidement et simplement des données dans une liste plus ou moins longue. Qui plus est, cela permet de charger plus rapidement la page et par la même occasion, d’éviter de créer des listes très longues.
Voici une représentation du widget en fonction :

Alors, pour le mettre en œuvre, il faut au préalable installer le plugin sfFormExtraPlugin qui l’intègre (l’installation est standard).
Pour bien comprendre la technique, je vais vous décrire chaque étape importante et vous fournir la structuration des données.
- le schéma de données (schema.yml)
options:
collate: utf8_general_ci
charset: utf8
Maitre:
connection: doctrine
columns:
titre:
type: varchar(255)
notnull: true
Eleve:
connection: doctrine
columns:
titre:
type: varchar(255)
notnull: true
id_maitre:
type: integer
notnull: true
relations:
Maitre: { onDelete: cascade, onUpdate: cascade, local: id_maitre, foreign: id }
J’ai ensuite généré ma base de données et deux modules d’admin generator avec Doctrine.
Je me retrouve donc avec un module de gestion des Maîtres et un autre des Elèves. Du fait du schéma, lorsqu’on rajoute un élève, on doit choisir son maître parmi la liste déroulante de maîtres. Nous placerons donc ici notre autocomplete.
Avant de l’intégrer, voici comment se présente le formulaire d’ajout d’élève :

La première chose qu’on travaille généralement dans ce cas est l’affichage d’un texte à la place des identifiants :
- lib/model/doctrine/Maitre.class.php
class Maitre extends BaseMaitre
{
public function __toString()
{
return $this->titre;
}
}
Ce qui nous donne :

Jusque là, rien de particulier mais la liste paraît assez longue tout de même (899 entrées !) et en environnement dev, cache vidé, cette simple page met plus de 0,8s à apparaître. C’est trop.
Remplaçons cette liste déroulante par un champ autocomplete !
- lib/form/doctrine/EleveForm.class.php
public function configure()
{
$this->widgetSchema['id_maitre']->setOption('renderer_class', 'sfWidgetFormDoctrineJQueryAutocompleter');
$this->widgetSchema['id_maitre'] = new sfWidgetFormDoctrineJQueryAutocompleter(
array(
'model' => "Maitre",
'url' => url_for("@ajax_maitre"),
'config' => '{ max: 50}'
)
);
}
On remarque qu’on remplace bien le widget d’origine (sfWidgetFormDoctrineChoice) par l’autocomplete.
On remarque également qu’on lui passe en option le modèle avec lequel travaillé et une route symfony mais qu’on peut également lui passer le nombre de lignes à afficher (ici 50).
Utilisant le helper Url, il faut penser à le charger ici :
public function configure()
{
sfProjectConfiguration::getActive()->loadHelpers('Url');
// ....
}
Il faut donc maintenant créer cette route
- apps/your_app/config/routing.yml
ajax_maitre:
url: /ajax-maitre
param: { module: maitre, action: ajaxMaitre }
Cette route est simple à comprendre : on charge la méthode executeAjaxMaitre du fichiers actions.class.php du module maitre.
Créons donc cette méthode alors !
- apps/your_app/modules/maitre/actions/actions.class.php
public function executeAjaxMaitre(sfWebRequest $request)
{
// si ce n'est pas une requête ajax, on renvoit vers du 404
$this->forward404unless($request->isXmlHttpRequest());
// c'est de l'ajax, on retourne donc du json
$this->getResponse()->setContentType('application/json');
$choices = array();
// on récupère les données de la base de données
$maitres = Doctrine::getTable('Maitre')->getMaitreAutocompletion($request->getParameter('q'), $request->getParameter('limit'));
// on boucle sur les données et on charge les données dans un nouveau tableau, celui qu'on va retourner
foreach($maitres->getData() as $m)
{
$choices[$m->id] = $m->titre;
}
// s'il y a des données, on retourne le tableau encodé en json
if($choices != array())
{
return $this->renderText(json_encode($choices));
}
}
Il nous reste à créer la méthode getMaitreAutocompletion du modèle Maitre
- lib/model/doctrine/MaitreTable.class.php
public function getMaitreAutocompletion($q, $limit){
$dq = Doctrine_Query::create()
->select("m.id, m.titre")
->from("Maitre m")
->where("m.titre LIKE ?","%".$q."%")
->limit($limit)
->orderBy('m.titre ASC');
return $dq->execute();
}
Dernière chose importante avant de tester, il faudrait peut-être intégrer jQuery non ? Commençons par télécharger la dernière version minifiée sur le site officiel, copions là dans web/js/jquery.min.js puis
- apps/your_app/config/view.yml
javascripts: [jquery.min.js]
That’s all folks !
Regardons alors le résultat :

Quoi de plus simple, je vous le demande !
Ainsi s’achève ce petit tutoriel qui, je l’espère, vous sera bien utile !
A bientôt pour de nouvelles aventures sur symfony !
EDIT : Merci à Jonathan T pour l’astuce concernant le nombre de lignes affichées lors de l’autocompletion !