Autocompletion avec ZendX_JQuery sur une grosse liste de données issues d’une base de données

Il existe des tutoriels assez bien faits pour insérer dans un formulaire Zend Framework une zone de liste en autocomplétion, en utilisant le composant ZendX_JQuery_Form_Element_Autocomplete, mais ceux-ci sont basés sur une courte liste de données dans un tableau PHP.

Ce petit tutoriel vous donne la solution pour avoir la même chose mais en allant chercher parmi une grosse quantité de données issues d’une base de données.

Je base cet exemple sur la recherche parmi les 36 682 communes françaises stockées dans une base MySQL mais ça doit être assez facilement adaptable à d’autres données.
Le code fourni fonctionne avec Zend Framework 1.11.1, JQuery 1.4.4, jquery-ui-1.8.7.custom.

1) Il faut, bien entendu, que JQuery soit installé et configuré pour fonctionner avec votre projet !

– J’ai ceci dans le layout (à adapter à votre version de la bibliothèque JQuery et à son emplacement) :

1
2
3
4
$this->JQuery()->setLocalPath($this->baseUrl() . '/js/jquery/development-bundle/jquery-1.4.4.js');
  $this->JQuery()->addStyleSheet($this->baseUrl() . '/js/jquery/development-bundle/themes/smoothness/jquery-ui-1.8.7.custom.css');
  $this->JQuery()->setUiLocalPath($this->baseUrl() .'/js/jquery/development-bundle/ui/jquery-ui-1.8.7.custom.js');  
  echo $this->jQuery();

– J’ai ceci dans le bootstrap :

1
2
3
4
5
6
7
8
9
  protected function _initJQuery()
  {
    $this->bootstrap('view');
    $view = $this->getResource('view');
    $view->addHelperPath('ZendX/JQuery/View/Helper/', 'ZendX_JQuery_View_Helper');
    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
    $viewRenderer->setView($view);
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
  }

2) Passons au code utile à l’autocomplétion en commençant par le modèle et en y créant une fonction qui extrait l’identifiant et le nom des communes à partir du début du nom :

1
2
3
4
5
6
7
8
9
10
11
12
  public function getCommunesParDebutNom($debut)
  {
    $debut = $debut.'%';
    $sql = "
      SELECT cmn_id, cmn_nom  
      FROM tr_commune_cmn  
      WHERE cmn_nom LIKE :debut  
    "
;
    $communestrouvees = $this->_db->fetchAll($sql, array('debut' => $debut));
   
    return $communestrouvees;
  }

3) Ensuite, ajoutons d’abord le helper d’action JQuery Autocomplete dans l’initialisation du contrôleur :

1
2
3
4
5
6
7
    public function init()
    {
      // Ajout du helper d'action JQuery autoComplete
      Zend_Controller_Action_HelperBroker::addHelper(
        new ZendX_JQuery_Controller_Action_Helper_AutoComplete()
    );
    }

4) Puis, toujours dans le contrôleur, créons une fonction qui va générer la source du champ autocomplete :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
  public function recherchecommunesAction()
    {
      $request = $this->getRequest();
      $debut = $request->getParam('term');
     
      $commune = new Application_Model_DbTable_Commune();
      $result = $commune->getCommunesParDebutNom($debut);
     
    $listecommunes = array();
   
    foreach($result as $cmn)
    {
      $listecommunes[$cmn['cmn_id']] = $cmn['cmn_nom'];
    }
    $this->_helper->json(array_values($listecommunes));
    }

=> L’action recherchecommunes fait appel à la fonction du modèle.

5) Passons au formulaire et créons l’élément autocomplete :

1
2
3
4
5
6
7
8
9
10
11
12
        $baseurl = Zend_Controller_Front::getInstance()->getBaseUrl();
 
    // Commune
    // FIXME Ne donne pour le moment que le nom de la commune et pas son identifiant
    $etb_id_commune = new ZendX_JQuery_Form_Element_AutoComplete('etb_id_commune');
    $etb_id_commune
      ->setLabel('Commune')
      ->setRequired(false)
      ->setFilters(array('StripTags'))
      ->setJQueryParam('autoFill', true)
      ->setJQueryParams(array('source' => $baseurl.'/gereretablissements/recherchecommunes')
              );

=> La source de l’élément autocomplete est l’url de l’action recherchecommunes (ici dans le contrôleur gereretablissements).
=> Remarquez aussi le FIXME… il me reste à peaufiner l’utilisation de JQuery ! Ce qui m’oblige pour le moment à avoir ce traitement des données postées par l’utilisateur dans le contrôleur :

1
2
3
4
5
6
        // Recherche de l'identifiant de la commune
        // FIXME A revoir quand le JQuery_Autocomplete retournera directement l'id
        $nom_commune = $form->getValue('etb_id_commune');
        $commune = new Application_Model_DbTable_Commune();
        $result = $commune->getCommuneParNom($nom_commune);
        $data['etb_id_commune'] = (int)$result['cmn_id'];

6) Rien de spécial dans la vue :

  echo $this->form->etb_id_commune;

9 réflexions au sujet de « Autocompletion avec ZendX_JQuery sur une grosse liste de données issues d’une base de données »

  1. C’est une bonne idée de faire un tuto qui gère Autocompletion avec ZendX_JQuery sur une grosse liste de données issues d’une base de données.

    J’ai pas bien compris la fin du tuto .Ou est ce qu’on créer le champs du formulaire .

    ça serais cool si on pouvais avoir le code source de ce petit projet

  2. J’ai pas bien compris la fin du tuto .Ou est ce qu’on créer le champs du formulaire

    Dans Zend Framework, tu crées le formulaire dans le sous-répertoire « forms » du répertoire « application ». J’ai ainsi, par exemple, un fichier Etablissement.php dans ce sous-répertoire qui contient entre autre ceci :

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    class Application_Form_Etablissement extends ZendX_JQuery_Form&nbsp;<br />
    {&nbsp;<br />
    &nbsp;       $this-&gt;setName('Établissement');&nbsp;<br />
    &nbsp;       $this-&gt;setMethod('post');&nbsp;<br />
    &nbsp;       &nbsp;<br />
    &nbsp;       $baseurl = Zend_Controller_Front::getInstance()-&gt;getBaseUrl();&nbsp;<br />
    &nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;// Commune&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;// FIXME Ne donne pour le moment que le nom de la commune et pas son identifiant&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;$etb_id_commune = new ZendX_JQuery_Form_Element_AutoComplete('etb_id_commune');&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;$etb_id_commune&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&gt;setLabel('Commune')&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&gt;setRequired(false)&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&gt;setFilters(array('StripTags'))&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&gt;setJQueryParam('autoFill', true)&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;-&gt;setJQueryParams(array('source' =&gt; $baseurl.'/gereretablissements/recherchecommunes')&nbsp;<br />
    &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;);&nbsp;<br />

    L’élément de formulaire $etb_id_commune est ensuite utilisé dans la vue views/scripts/gereretablissements/ajouter.phtml qui contient entre autre ceci :

    1
    &nbsp;&nbsp;echo $this-&gt;form-&gt;etb_id_commune;

    Ça répond à ta question ?

  3. Oui votre réponse répond bien à ma question . Mais quand je met le bout de code ci dessous dans mon bootstrap aucune page ne s’affiche.

    protected function _initJQuery()
    {
    $this->bootstrap(‘view’);
    $view = $this->getResource(‘view’);
    $view->addHelperPath(‘ZendX/JQuery/View/Helper/’, ‘ZendX_JQuery_View_Helper’);
    $viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer();
    $viewRenderer->setView($view);
    Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
    }

    voici ce que j’ai déjà dans mon bootstrap

    function _initViewHelpers()
    {
    $this->bootstrap(‘layout’);
    $layout = $this->getResource(‘layout’);
    $view = $layout->getView();

    $view->setHelperPath(APPLICATION_PATH.’/helpers’,  »);
    ZendX_JQuery::enableView($view);
    $view->doctype(‘HTML4_STRICT’);
    $view->headMeta()->appendHttpEquiv(‘Content-type’, ‘text/html;charset=UTF-8′)
    ->appendName(‘description’, ‘Using view helpers in Zend_view’);

    $view->headTitle()->setSeparator(‘ – ‘)
    ->headTitle(‘Medinour’);

    }

    Comment faire pour que ça fonctionne très bien?

  4. Le bootstrap en lui-même n’affiche rien. Il faut l’ensemble du code pour voir apparaître quelque chose !

    Comme le code fonctionne chez moi, je ne sais pas pourquoi il ne fonctionne pas chez toi. Le mieux serait je pense de poster une nouvelle discussion dans le forum avec tout le code nécessaire et la version de Zend_Framework que tu utilises, comme je l’avais fait moi-même avant d’aboutir à cette solution.

  5. Salut CinéPhil,

    C’est un bon tut que tu nous as fait ;-)
    Je l’ai implémenté pour faire une auto-complétion qui affiche le CP et la ville!:
    public function recherchevillesAction()
    {
    $request = $this->getRequest();
    $debut = $request->getParam(‘term’);

    $ville = new Application_Model_DbTable_Villes();
    $result = $ville->getVillesParDebutNom($debut);

    $listevilles = array();
    //$listecommunes[0] =  »;

    foreach($result as $cmn)
    {
    $listevilles[$cmn[‘id’]] = $cmn[‘code_postal’] . » « . $cmn[‘article_min’] . $cmn[‘ville_min’];
    //$listevilles[$cmn[‘id’]] = $cmn[‘code_postal’];
    }
    $this->_helper->json(array_values($listevilles));
    }

    (article_min et ville_min pour l’article et la ville en minuscule).

    Ca marche nickel chrome!

    Question:
    Avec ta version, comment gères tu les communes qui ont le même nom?

    Je butte sur la récupération de l’ID de ma commune, pour l’enregistrer dans ma table utilisateur.

    Ne pourrait-on pas remplir un champs de type hidden avec l’id de la commune?
    Il y aurait des petites améliorations à faire sur cette classe ;-)

  6. Oui effectivement, c’est encore à améliorer, comme le montrent les commentaires dans mon code !
    Je m’y remettrai sans doute vers septembre ou octobre car mon projet a un peu changé de cible. Il faudra que j’étudie JQuery plus attentivement pour comprendre comment avoir plusieurs colonnes afin de différencier les villes homonymes et récupérer leur identifiant.

    Ceci dit, le principe que j’ai donné peut fonctionner avec n’importe quelle liste issue d’une BDD.

    Quand j’aurai peaufiné la chose, je ferai un tutoriel plus structuré.

    Merci pour le commentaire.

Laisser un commentaire