Custom Route classes and Pagination in CakePHP

I recently ran into a tricky issue with the built in Paginator Helper when combined with custom routes I was using in a custom CMS driven site I'm building.

I have the site setup so that admins can create Category pages with unique URL slugs so that they can edit pages and also get nice URLs like so:

I've set this up in CakePHP using a custom Route class as per the docs and this works great. Below I've got the routes.php code to check for a Category slug and use the view action of the ProductCategories Controller.

// app\Config\routes.php file
Router::connect('/:slug/*', array('controller' => 'product_categories', 'action' => 'view'), array('routeClass' => 'CategorySlugRoute'));

Here's the Route class code which is pretty straight forward just in case you need to do anything similar. I've added caching to speed things up and the only caveat are the sub-categories; as these are separated with a forward slash we have to rebuild them to ensure we're checking the slugs for sub categories as well as top level categories.

 * Deal with Category Slugs
App::uses('ProductCategory', 'Model');
class CategorySlugRoute extends CakeRoute {

	 * Parse the URL
	 * @param string $url
	 * @return boolean
	function parse($url) {
		$params = parent::parse($url);
		if (empty($params)) {
			return false;

		// See if slugs are cached
		$slugs = Cache::read('slugs_categories');
		if (!$slugs) {
			// Get all slugs
			$ProductCategory = new ProductCategory();
			$slugs = $ProductCategory->find('list', array(
				'fields' => array('ProductCategory.slug'),
				'recursive' => -1

			Cache::write('slugs_categories', $slugs);

		// Reverse slugs for easy comparison
		$slugs = array_flip($slugs);

		// See if sub categories have been passed
		if (!empty($params['pass'])) {
			$params['slug'] .= '/' . implode('/', $params['pass']);

		// Match passed slug with Category slugs
		if (isset($slugs[$params['slug']])) {
			return $params;

		return FALSE;

When it came to paginating those result it wasn't clear how to take advantage of the custom slug I was using. I tried a few different solutions however all the generated pagination links all included the /product_categories/view in the URL which is incorrect.

To fix the links I had to pass in the slug as a url Paginator option before the calls to generate the links. After this CakePHP knew what was going on and correctly built the correct URL.

// app\View\ProductCategories\index.ctp
$this->Paginator->options(array('url' => array('slug' => $category['ProductCategory']['slug'])));
echo $this->Paginator->prev('Prev');
echo $this->Paginator->numbers();
echo $this->Paginator->next('Next');

Posted on 11th July 2013
3 years, 7 months, 2 weeks, 3 days ago

comments powered by Disqus