Pagination avec critères de filtrage complexes
Prenons l’exemple d’un blog classique, sur lequel nous désirons mettre en place un moteur de recherche pour faciliter l’accès aux articles à nos visiteurs. Nous ne souhaitons cependant pas créer de vue spécifique pour l’affichage des résultats mais simplement réutiliser une vue existante qui affiche la liste paginée des articles grâce au Paginator.
1. Modèle de données
Notre blog propose des articles (posts), composés d’un titre (title), d’un corps de message (content), rédigés par des auteurs respectifs (user_id). Nous souhaitons proposer à nos visiteurs la possibilité d’effectuer une recherche à la fois sur le titre et sur l’auteur d’un article.
2. Formulaire de recherche
Supposons que notre blog dispose d’ores et déjà d’une page listant les articles à l’adresse suivante : /posts/archives, soit l’action archives du Contrôleur posts_controller. Nous souhaitons intégrer notre moteur de recherche au sein de cette vue, afin de retenir les critères de recherche entrés par le visiteur. Autrement dit, nous allons filtrer notre liste d’articles en fonction des critères de recherche.
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 | // {app}/views/posts/archives.ctp echo $form->create( 'Article', array( 'action' => 'search', 'type' => 'GET' ) ); // Champ texte pour les mots clés echo $form->input( 'title', array( 'label' => 'Entrez un terme', 'value' => isset($this->params['named']['title']) ? $this->params['named']['title'] : null ) ); // Champ select avec la liste des auteurs echo $form->input( 'user', array( 'label' => 'Selectionnez un auteur', 'type' => 'select', 'options' => $userList, 'selected' => isset($this->params['named']['user']) ? $this->params['named']['user'] : null, 'empty' => 'Selectionnez un auteur' ) ); echo $form->end('Lancer la recherche'); |
3. Traitement des critères
Récupérons à présent les données entrées dans le formulaire dans le Contrôleur PostsController :
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 | // app/controllers/posts_controller.php function search() { // exclusion du 1er argument de $this->params['url'] (l'URL courante) array_shift($this->params['url']); $passedArgs = array(); // exclusion des données nulles $criterias = array_filter( $this->params['url'], create_function( '$item', 'return !empty($item);' ) ); // transformation des données au format du Paginator foreach($criterias as $key => $value) { array_push($passedArgs, $key . ':' . urlencode($value)); } // redirect vers la Vue, en passant en argument les critères de recherche retenus. $this->redirect('archives/'.join('/', $passedArgs)); } |
Les données entrées par l’utilisateur ont été transformées dans un format compréhensible par le Paginator puis redirigées vers l’action de notre choix listant déjà nos articles. Il faut à présent modifier la partie listant nos articles afin de prendre en compte les critères de recherche passés en argument.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | //app/controller/posts_controller.php function index() { // Liste des auteurs d'article $this->set('userList', $this->Post->User->find('list')); // Nettoyage de la saisie App::import('Sanitize'); $Sanitizer =& new Sanitize(); $Sanitizer->clean(&$this->params['named']); // Utilisons le deuxième argument de la méthode Paginate // pour fournir un tableau de conditions que nous construirons // dans notre modèle à l’aide de la méthode createStatement $articles = $this->Paginate('Post', $this->Post->createStatement($this->params['named'])); $this->set('articles', $articles); } |
Nous transmettons à une méthode de notre modèle les paramètres de recherche, celle-ci aura pour but de transformer les paramètres sous forme de tableau de critères SQL compréhensibles par CakePHP.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | // app/models/Post.php function createStatement($data) { $statement = array(); if(isset($data['title'])) { array_push($statement, array("{$this->name}.title" => 'LIKE %' . $data['title'] . '%')); } if(isset($data['user'])) { array_push($statement, array("{$this->name}.user_id" => $data['user'])); } return $statement; } |
Nous disposons à présent d’une liste d’articles filtrés selon les critères entrés par l’utilisateur !
Reste à imposer à notre vue ( /app/views/posts/archives.ctp ) de retenir les critères entrés lors de l’utilisation du Helper Paginator. Pour cela, il suffit simplement de transmettre l’URL courante comme paramètre.
Au début du fichier app/views/posts/archives.ctp ajoutons :
1 2 | //app/views/posts/archives.ctp options(array('url' => isset($this->params['named']) ? $this->params['named'] : array() )); |
Voilà, nous disposons à présent d’un moteur de recherche utilisable par le Paginator, nous pouvons présenter les résultats sur plusieurs pages et les trier.
L’utilisation de cette méthode présente trois avantages :
- Nous sommes libres de masquer les champs de recherche dans l’URL, contrairement aux URLs générées par le Paginator. Pour reprendre notre exemple, nous aurions très bien pu remplacer l’URL suivante :
/posts/archives/user:3/title:un+super+articlepar
/posts/archives/author:3/message:un+super+articleet simplement changer les tableau de correspondance dans la fonction
createStatement()du modèle. - Les recherches entrées peuvent très bien être mises en favoris. Imaginez l’url suivante :
/articles/sort:price/direction:desc/currency:dollar/title:dvd+collector - Nous aurions tout à fait pu adapter et deplacer les méthodes
search()etcreateStatement()respectivement dansapp_controller.phpetapp_model.phppour proposer une fonction recherche sur chacun de nos modèles !
Christophe Cholot
Bonjour Christophe,
Comment utilises-tu les paramètres passés dans l’url avec le paginator ?
Ton dernier exemple manque de détail dans archives.ctp.
Merci de ton retour
8 mars 2008 à 13:48
Auteur : Fred