Voici un petit bout de code permettant de résoudre un phénomène dû à l’utilisation des routes avec CakePHP. Lors d’une question récemment posée sur IRC, un membre a soulevé une remarque intéressante vis-à-vis du système de routage de Cake : lorsqu’une route existe pour une page, rien n’empêche l’utilisateur d’accéder à la page en utilisant l’url d’origine « /controller/action/param1/param2« .
Ci-après une illustration du problème avec le contrôleur « Pages » livré avec CakePHP, ainsi qu’une proposition de code pour rediriger correctement les utilisateurs. C’est l’occasion idéale pour découvrir quelques méthodes proposées par le Router de CakePHP.
Le problème
Prenons l’exemple du contrôleur « Pages » qui est livré avec CakePHP. Dans une nouvelle installation, la page d’accueil de l’application est la Page « home » correspondant à l’action « display » du contrôleur Pages. Ainsi, lorsque l’on accède à l’url http://monsite.com/ nous voyons en fait la page http://monsite.com/pages/display/home … ceci est dû à la route suivante, déclarée dans « /config/routes.php » :
Router::connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
Mais rien n’empêche un utilisateur d’accéder à cette même page directement depuis l’url http://monsite.com/pages/display/home. Dans la plupart des cas, c’est assez peu important mais vous pouriez vouloir éviter ceci pour des raisons de référencement (duplicate content, changements d’url) ou autre.
Une solution
La solution suivante consiste à analyser l’url actuelle pour déterminer quelle action est appelée, puis de vérifier si l’url correspond aux routes déclarées. Si l’url ne correspond pas il suffit simplement de faire une redirection 301 vers l’url correcte.
Dans la méthode beforeFilter de votre AppController, ajoutez l’instruction suivante :
/** * Before filter callback * * @return void * @access public */ public function beforeFilter() { $this->_checkRoute(); }
Et créez la méthode « _checkRoute » (et une fonction bonus pour détecter les appels avec requestAction()) dans votre AppController, avec l’implémentation suivante :
EDIT: Voici une version encore plus simple et qui fonctionnera mieux! (voir les commentaires, merci Rob2 pour la remarque)
/** * Check that the current page has been accessed through the correct url. * When current url does not match the correct route, user is redirected to the correct location * * @return void * @access protected */ protected function _checkRoute() { $url = Router::url($this->passedArgs); if ($this->here != $url && !$this->isRequestedAction() && !$this->RequestHandler->isAjax()) { $this->redirect($url, 301); } } /** * Returns true if the action was called with requestAction() * * @return boolean * @access public */ public function isRequestedAction() { return array_key_exists('requested', $this->params); }
Voici tout de même la version initiale, pour information
protected function _checkRoute() { $urlParts = Router::parse($this->here); $url = array_merge($urlParts, $urlParts['pass'], $urlParts['named']); unset($url['pass'], $url['named']); if ($this->here != Router::url($url)) { $this->redirect($url, 301); } }
… et le tour est joué !
Petite analyse
La première ligne décode l’url actuelle ($this->here) pour récupérer chaque partie dans un tableau. Ensuite, les paramètres nommés et non-nommés sont ajoutés au tableau de base décrivant l’url afin que celui-ci puisse être utilisé pour générer l’url correcte de la page.
Si l’url générée est différente de l’url actuelle l’utilisateur est redirigé au bon endroit.
Pour information, je n’ai jamais utilisé ce code dans une application mais n’ai fait que quelques tests basiques pour vérifier son bon fonctionnement. Si vous l’utilisez et trouvez des problèmes, n’hésitez pas à me les parvenir dans les commentaires
Pingback: CakePHP : signets remarquables du 06/11/2009 au 09/11/2009 | Cherry on the...