Catégories
PHP

Pagination, Rewriterule et CakePHP

Ceux qui utilisent CakePHP ont deja ete confronte au fameux probleme de la pagination combine a l’utilisation de l’url-rewriting (et quel cauchemar !!!).

Un petit recapitulatif pour ceux qui arrivent sur cette page. Le framework est livre avec une magnifique methode de pagination qui dechire, lorsque l’on reste dans l’utilisation classique de CakePHP : /controller/action/param … je vous laisse lire le cookbook qui est une mine d’or pour tout ce qui se rapporte a la pagination.

OK, donc j’ecris un billet pour dire que la pagination de CakePHP est terrible, c’est cool non 🙂 En fait pas tant que ca.

Tout ceux qui veulent sortir des sentiers battus, et donc combiner l’url-rewriting avec la pagination integree de CakePHP vivent generalement un moment assez desagreable. En effet, l’url-rewriting => OK, la pagination => OK, les 2 ensembles ouch, ce n’est pas tellement ce a quoi on s’attendait. Plutot que de continuer a parler pendant des heures, entrons directement dans le vif du sujet.

Les points forts de la pagination :

– La methode et le helper font tout pour nous, avec une certaine flexibilite dans le helper paginator.
– C’est deja pas mal non …

Les points faibles de la pagination (et oui il y en a, sinon vous ne seriez pas ici) :

– Au niveau du SEO, on va se retrouver avec un DC sur la page /controller/action et /controller/action/page:1
– Le dernier parametre page:pg_nb n’est pas tres url-friendly.

Aux grands maux, les grands remedes :

Pre-requis (routes correctement configurees dans /app/config/routes.php) :

// Si vous utilisez l'extension .html pour vos pages
Router::parseExtensions('html');
// N'oublions pas de connecter le param page
// Meme si je ne sais pas si cette etape est indispensable
Router::connectNamed(array('page'));

– On va d’abord s’attaquer au htacces afin d’avoir des urls du type /controller/action/page_nb.html

# {app}/webroot/.htaccess
# La tout depend de la structure de vos urls, je met cette ligne 
# a titre indicatif, a vous de l'adapter
RewriteRule ^([a-zA-Z-_]+)/([a-zA-Z-_]+)/page([0-9]+)\.html$ 		/$1/$2/page:$3					[L]

– On va ensuite modifier le comportement du helper paginator (oui, sinon ca ne servira pas a grand chose)

<?php
/* 
On veut recuperer la totalite de l'url
Si l'url est du type /articles/seo/page2.html, on souhaite garder
uniquement /articles/seo car CakePHP, meme en definissant bien vos routes
et votre htaccess, utilisera sa pagination du style /controller/action/page:1
*/
$url	= preg_replace('/\/page([0-9]+)\.html/', '',FULL_BASE_URL.$_SERVER['REQUEST_URI']);
/* 
Idem pour une page comme /articles/seo.html (qui est en fait la page1) ou on ne gardera que
/articles/seo
*/
$url	= preg_replace('/\.html/', '',$url);
// On utilise l'url generee precedement
options = array('url' => $url);
// Et afficher les pages
e($paginator->numbers());
?>

– On garde alors le meilleur pour la fin ……. La modification du helper paginator.
Alors pour cette partie, j’aurais pu surcharger la methode link de la class PaginatorHelper, mais j’avoue que je n’ai pas essayer, j’ai donc tout simplement modifier la methode directement dans le fichier /cake/libs/view/helpers/paginator.php aux alentours de la ligne 276.
Donc la methode avant mon passage :

276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
function link($title, $url = array(), $options = array()) {
		$options = array_merge(array('model' => null, 'escape' => true), $options);
		$model = $options['model'];
		unset($options['model']);
 
		if (!empty($this->options)) {
			$options = array_merge($this->options, $options);
		}
		if (isset($options['url'])) {
			$url = array_merge((array)$options['url'], (array)$url);
			unset($options['url']);
		}
		$url = $this->url($url, true, $model);
 
		$obj = isset($options['update']) ? 'Ajax' : 'Html';
		$url = array_merge(array('page' => $this->current($model)), $url);
		$url = array_merge(Set::filter($url, true), array_intersect_key($url, array('plugin'=>true)));
		return $this->{$obj}->link($title, $url, $options);
	}

Et la methode apres mon passage :

276
277
278
279
280
281
282
283
284
285
286
287
288
289
function link($title, $url = array(), $options = array()) {
		$options = array_merge(array('model' => null, 'escape' => true), $options);
		$model = $options['model'];
		unset($options['model']);
 
		if (!empty($this->options)) {
			$options = array_merge($this->options, $options);
		}
		// On ne veut pas de page1, pour eviter le DC, donc
		$url_rw = ($url['page'] != 1) ? $options['url'].'/page'.$url['page'].'.html' : $options['url'].'.html';
 
		$obj = isset($options['update']) ? 'Ajax' : 'Html';
		return $this->{$obj}->link($title, $url_rw, $options);
	}

Voila, je sais qu’a l’epoque ou je cherchais une astuce, j’etais uniquement tomber sur des feintes en Ajax ou autre, ce qui ne m’interessait absolument pas. Voila pourquoi j’ai du mettre les mains dans le camboui pour feinter ce filou de CakePHP.

P.S. : Cette methode a ete testee et fonctionne sur CakePHP 1.2.6, n’a pas ete encore teste sur la 1.3

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *