Thursday, February 18, 2010

CakePHP Pagination & Custom Routes Issue

Custom Routes are something that make CakePHP very interesting! If you are reading this post you must have experimented with what we call custom routing. The problem might arise when we want to paginate models which use custom routing.

The problems may be many-folded which all simplifies to one simple issue:

UNFORMATTED URL
Say, you want to display url in the following format using custom routes

CakePHP default url structure
http://example.com/categories/index/categoryID

Your WELL FORMATTED URL structure:
http://example.com/category-name-ID

Obviously, your custom route element points to 'index' action of 'categories' controller with two parameters - category-name and 'categoryID'.

Router::connect(

 '/:slug-:id/*',
  array('controller' => 'categories', 'action' => 'index'),
  array(
  // order matters
  'pass' => array('slug','id'),
 'id'=> '[0-9]+'
  )
  );

This works perfectly for URLs like
http://example.com/my-first-category-ID1
http://example.com/my-second-category-ID2

But it may cause problem when you try to paginate

Cake will not pick  your params (here 'slug' and 'id') passed through url if you do not force Cake to do so while paginating.  

To fix this issue, you MAY use:

 $paginator->options(array('url' => $this->passedArgs));  
or, as I have told here earlier following CakePHP book.

But this will format your paginated urls like
http://example.com/categories/index/param1/param2/page:xxxx

You can obviously browse pages with the above url. But it does not look decent. So, your entire effort with custom routing might just not work. 

You may still get a bad URL.

DO NOT WORRY!

While defining paginator options in your view file, follow religiously $html->link() structure. For an example I have put forward the structure of  $paginator->options() below in a view file:

file:// /app/views/categories/index.php

$paginator->options(array('url'=> array(
'controller' => 'categories', 
  'action' => 'index',
'slug'=>$this->params['slug']),
'id'=>$this->params['id'])
  ));

Now Cake will make well formatted URL automatically, and your paginated url should look like

http://example.com/category-name-id1/page:2
http://example.com/category-name-id1/page:3

Done!
I hope it helps someone.
Does it?

Please Note
I have marked asterisk symbol (*) with RED color while talking about custom route elements. This (*) MUST be there for pagination to work properly. 
Good night.

10 comments:

M. Ersin said...

thanks for this information!

Nilz said...

you are welcome.

Denis P. said...

thanks ! for your help.

I posted a same/inspired solution on my blog in french.

french inspired version

Nilz said...

Checked your post. It is really useful.

Peter said...

Just to note, I came accross the same problem earlier, but this was using a route in a plugin, I found the route would take on the plugin name before the controller name, or even the name you override the contoller with. To get past this just add 'plugin' => null to the paginator options

You said...

Thanks Peter. Yes. You are quite right.

amit said...

Nice tutorial....thanks for this post.....
Please visit this link for learn more script.
http://amitmondal.wordpress.com/

Manu said...

Thanks a lot for this article, I had trouble with my pagination routes and passing the url manually worked perfectly in the end :)

Siddhartha said...

Thanks Manu. I am glad to know that it helped you.

Max said...

Hi man. Thank you for the article. Could you please help me because it does not work for me.

My routes code is:
Router::connect('/:type-:id/:slug', array('controller' => 'Registers', 'action' => 'view'),array('pass' => array('type', 'id', 'slug')));

My paginator option code is:
$this->Paginator->options(
array('update'=>'#CustomerPaging',
'url'=> array(
'controller' => 'registers',
'action' => 'view',
'slug'=>$this->params['slug']),
'indicator' => '#LoadingDiv'));

The result url is: /registers/view/page:value/slug:value

So cake does not formatting the url automatically. Any help will be appreciated.