La souplesse des Routes avec CakePHP 1.3
La version 1.3 de CakePHP offre encore plus de souplesse dans la gestion des Routes. Nous allons l’illustrer avec une application toute simple : des Posts avec un id, un titre, un slug (que l’on peut gérer en toute simplicité avec le Comportement SluggableBehavior) et un contenu.
Nous désirons avoir des url très simples du style www.monsite.tld/slug-du-post. L’idée est de pouvoir créer des Routes de façon dynamique sans avoir à les écrire à la main dans le fichier config/routes.php. CakePHP 1.3 permet de créer une classe à brancher sur le Router qui va analyser une url, rechercher le slug passé en paramètre dans les Posts, et renvoyer sur l’action view du contrôleur PostsController avec le slug du Post en paramètre. Afin de garantir une vitesse de traitement maximale, nous allons mettre la liste des slugs possibles en cache.
Le Modèle Post
Le modèle Post va accueillir une méthode cacheSlugs() qui retourne tous les slugs possibles et met cette liste en cache. Le Modèle va également redéfinir les callbacks afterSave() et afterDelete() pour recréer ce cache en cas de création, mise à jour ou suppression d’un Post.
<?php class Post extends AppModel { var $displayField = 'title'; var $validate = array( 'title' => array( 'notEmpty' => array( 'rule' => 'notEmpty', 'required' => true, 'allowEmpty' => false, 'message' => "Vous devez saisir un titre." ), 'unique' => array( 'rule' => 'isUnique', 'message' => "Ce titre est déjà attribué à une autre Post." ), ) ); function cacheSlugs() { Cache::delete('post_slugs'); $posts = $this->find('all', array( 'fields' => array('slug'), 'recursive' => -1 )); $slugs = Set::extract('/Post/slug', $posts); Cache::write('post_slugs', $slugs); return $slugs; } function afterSave() { $this->cacheSlugs(); } function afterDelete() { $this->cacheSlugs(); } } ?>
Une classe pour étendre CakeRoute
La grande nouveauté de la version 1.3 est la possibilité d’étendre la classe CakeRoute et de redéfinir les méthodes parse() et match() du Router principal. La méthode parse() décompose l’url appelée en paramètres avant de les passer à l’action du controller correspondante, tandis que la méthode match() fait l’inverse, elle prend en entrée des paramètres et construit l’url conformément aux règles définies par les Routes.
Nous allons créer un nouveau fichier {app}/libs/routes/post_route.php (le concept de « librairie » est aussi nouveau dans la 1.3).
<?php class PostRoute extends CakeRoute { function parse($url) { $params = parent::parse($url); if(empty($params)) { return false; } $slugs = Cache::read('post_slugs'); if(empty($slugs)) { App::import('Model', 'Post'); $Post = new Post(); $slugs = $Post->cacheSlugs(); } if(isset($params['slug']) && in_array($params['slug'], $slugs)) { return $params; } return false; } } ?>
Nous redéfinissons donc la méthode parse(), voyons ce qui se passe :
- Nous décomposons les paramètres contenus dans l’url en appelant la méthode parente
parse(), et nous sortons tout de suite si aucun n’est trouvé. - Nous lisons le fichier de cache contenant la liste des slugs de posts possibles : si rien n’est lu, nous importons le Modèle Post et appelons la méthode
cacheSlugs(), qui met en cache la liste des slugs et la renvoie. - Si le slug de l’url est bien l’un des slugs possibles, nous retournons les paramètres de l’url, et le Router va bien appeler l’action view du controller PostsController avec le slug en paramètre.
- Si rien ne s’est passé, nous sortons de la méthode avec false.
Appel de la classe PostRoute dans le Router
Nous pouvons maintenant créer l’unique Route qui va gérer tous les posts dans le Router, c’est-à-dire dans le fichier config/routes.php.
<?php // Accueil Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home')); // Posts App::import('Lib', 'routes/PostRoute'); Router::connect( '/:slug', array( 'controller' => 'posts', 'action' => 'view' ), array( 'routeClass' => 'PostRoute' ) ); ?>
Voyons les trois paramètres de cette Route :
- Nous définissons un paramètre nommé
slug, que nous allons préciser dans le 3ème paramètre de la Route. - Si l’url ressemble au premier paramètre, nous envoyons sur le controller PostsController, dans l’action view.
- Ici nous précisons ce que le premier paramètre,
slug, veut dire :- Nous disons au Router d’utiliser notre classe PostRoute pour traiter cette Route-ci en particulier. Si la méthode
parse()de la classe PostRoute renvoie faux (la suite de caractères identifiée pour l’instant comme un slug ne correspond pas à un post existant), le Router continue son chemin et appelle la route suivante, sinon il envoie bien sur l’action définie en 2ème paramètre, dans notre cas sur l’action view du PostsController.
- Nous disons au Router d’utiliser notre classe PostRoute pour traiter cette Route-ci en particulier. Si la méthode
Faire un lien vers un Post
Voyons pour terminer comment faire un lien dans une vue vers un Post, il suffit de passer la clé ‘slug’ en paramètre de la méthode link() du Helper Html :
echo $this->Html->link($post['Post']['title'], array( 'controller' => 'posts', 'action' => 'view', 'slug' => $post['Post']['slug'] ));
Commentaires
6 avril 2010 à 18:30
Pratique cette nouvelle fonctionnalité. J’ai juste une question au niveau du cache, est-il possible de n’ajouter ou supprimer au cache que le slug du post en cours pour éviter de charger à chaque fois tous les posts et réécrire tous les slugs en cache ?
12 avril 2010 à 10:47
Tu ne peux pas manipuler simplement le fichier de cache avec le FileEngine proposé par défaut dans Cake, mais tu peux développer ta propre classe de caching afin de pouvoir modifier le contenu d’un fichier de cache sans avoir à le détruire et à le recréer complètement comme ici.
30 juin 2010 à 12:06
Bonjour,
Merci pour cet article qui tombe à pic!
je ne sais pas si j’ai loupé qqch, mais comment récupérer le slug dans la méthode view du controller?
J’ai essayé le
function view($slug){
}
mais le slug est vide.
merci d’avance pour la réponse
Angelo
30 juin 2010 à 13:00
Avec cette méthode le slug se trouvera dans $this->params['slug'].
30 juin 2010 à 13:02
Merci pour la rapidité de réponse!
10 octobre 2010 à 15:33
Merci pour cet article !
Je viens de tomber sur un autre article intéressant de CakeDC, qui proposent une sous-classe de CakeRoute : http://cakedc.com/fre/pierre_martin/2010/08/05/i18n-routes-with-cakephp-1-3
29 juillet 2011 à 1:11
Bonjour,
J’ai un grand problème par rapport à l’utilisation des routes,je travail avec la version 1.3.7 de Cakephp et j’utilise ce code dans rooter.php : Router::connect( ‘/membres/modifier/*’, array(‘controller’ => ‘members’, ‘action’ => ‘modifier’, ‘prefix’ => ‘membres’) ); je veux utiliser une redirection avec la fonction « redirect() » mais ça marche pas et je ne sais pas comment le faire ?
Merci de m’aider Yassine