Fancy routing examples with CakePHP 1.2

Routing in CakePHP is quite flexible in how you can route your urls to your controllers and actions. Offering both variable replacement and regex routing. You can route almost any parameter that is set by dispatcher and more. So lets try a few of these.

Replacing the admin routing

There are times when you don’t want the admin prefix. Now you could just change the admin prefix in your core.php. But then you need to refactor all your admin methods. Or you could set some fancy routing. Perhaps you want some admin prefixed methods and some non-admin prefixed methods. Say we had a few moderation actions that we didn’t necessarily want to have admin in front of it.

Show Plain Text
  1. Router::connect('/moderator/comments/:action', array('controller' => 'comments',
  2.                                                      'admin' => true));

This would connect any comments action to the comments controller with admin routing activated.

Turn on Extension based routing without using an extension.

As covered in a previous article RssHelper . You can turn on webserivce routing with out the extensions. Freeing your URL’s from file extensions.

Show Plain Text
  1. Router::connect('/posts/feed', array('controller' => 'posts',
  2.                                      'action' => 'index',
  3.                                      'url' => array('ext' => 'rss') ));

This route is an example of controlling not so normal route information. Basically it seems that you can set any parameter that shows up in your controllers $this->params through a route. By specifying the a structure similar to how it comes out in the $this->params in a route it will be set in your controller.

Use a Regular Expression

Regular expressions are super nifty and allow you to create advanced patterns that you can match requests against. If you want to learn more about Regular expressions

Show Plain Text
  1. //Get ID's that are numeric and 1-3 characters in length
  2.  
  3. Router::connect('/posts/view/:id', array(), array('id' => '[0-9]{1,3}' ));
  4.  
  5. //Feed animals only if they are a bat, cat, or rat
  6. Router::connect('/animals/feed/:type',
  7.                     array('controller' => 'pets',
  8.                           'action' => 'feed'),
  9.                     array('type' => '[r|b|c]at')
  10.                 );

Both of these routes give examples of how to use regular expressions with routing. The optional 3rd parameter of connect, allows you to define simple regular expresssion (no capture groups) to define what is a valid parameter. In the first example we define ID as a number that is between 1-3 characters. The second route will match /animals/feed/rat, /animals/feed/bat or /animals/feed/cat but not /animals/feed/cow. No food for the cows today.

So that is just a few examples of how you can do fancy routing in CakePHP.

Comments

Hi Mark,

Nice website and article.

Question, is there a way I can use an if/else statement in the routes? For example, if “animals/feed/cow” did show up in the URL, could cow be a controller function?

So if the URL matches the regexp, else assume it is a controller function.

anonymous user on 10/14/08

Rahil: You can use conditional statements in your routes as it is just PHP however, you will not be able to access any cake params, as they have yet to be determined. You would basically have to do regexp on the entire url. You could also make two routes that use differently placed :controller variables, combined with a few regexpit should work.

mark story on 10/15/08

Is it me or the url ext not working? You still have to add the extension to the end of the url for it to work.

anonymous user on 10/15/08

Alright I figured out the problem, you needed to add:

Router::reload();

Above the route for url extensions (e.g..the rss feed router)

anonymous user on 10/15/08

Actually doing the Router::reload() it breaks all other routes..

anonymous user on 10/16/08

Do Follow Directory: Router::reload() will reset the Router and really shouldn’t be used outside of testing situations.

If you have Router::parseExtensions() in your routes.php you will need to remove it before you can make routes with the ext specified. I haven’t had time to figure out why this is, but it works.

mark story on 10/18/08

Hi Mark, did yiou figured it out how this works with Router::parseExtensions(‘rss’)and ‘url’ => array(‘ext’ => ‘rss’))

Sharkoon on 1/11/09

HI,

in the beginning of your article you say “Perhaps you want some admin prefixed methods and some non-admin prefixed methods”.

In my site I would like to use non-prefixed methods but I need a way to recognise that I am in the admin area.

Is it possible to do this without the admin prefix? Can I write a generic rule to map all the “/admin” requests to their respective controllers and actions together with an ‘admin’ parameter?

Thanks in advance!!

Dario

Dario on 8/7/09

Hi Mark,

Nice article. I need a help. I want to do this:

domain.com/category/ -> categories controller > view.

domain.com/category/subcategory -> subcategories controller > view. (this page has sorting/pagination feature)

domain.com/category/subcategory/product-title -> products controller > view.

So I did this in my Router (I know I am wrong):

// category routing
Router::connect(
    '/:catslug',
    array('controller' => 'categories','action' => 'view'),
    array(
		'pass' => array('catslug')
	)
);
// subcategory routing
Router::connect(
    '/:catslug/:subcatslug/*',  // it may(/may not) contain sort/direction/limit
    array('controller' => 'sub_categories', 'action' => 'view'),
    array(
		'pass' => array('catslug','subcatslug')
	)
);
// product routing
Router::connect(
    '/:catslug/:subcatslug/:prodslug/*',  // this does not work cos i've already defined the route for  
    array('controller' => 'products', 'action' => 'view'),  //                  /:catslug/:subcatslug/*
    array(
		'pass' => array('catslug','subcatslug','prodslug')
	)
);

The last route for product does not work. I know it should not as I’ve already defined ‘/:catslug/:subcatslug/*’.
Only the category and subcategory routing works.

Please tell me how can I do the product routing… It’s urgent. Any kind of help will be appreciate.

Thanks

adnan on 1/27/10

Here is the code again.. for better reading:

// category routing
Router::connect(
    '/:catslug',
    array('controller' => 'categories','action' => 'view'),
    array(
		'pass' => array('catslug')
	)
);
// subcategory routing
Router::connect(
    '/:catslug/:subcatslug/*',  // it may(/may not) contain sort/direction/limit
    array('controller' => 'sub_categories', 'action' => 'view'),
    array(
		'pass' => array('catslug','subcatslug')
	)
);
// product routing
Router::connect(
    '/:catslug/:subcatslug/:prodslug/*',  // this does not work cos i've already defined the route for  
    array('controller' => 'products', 'action' => 'view'),  //                  '/:catslug/:subcatslug/*'
    array(
		'pass' => array('catslug','subcatslug','prodslug')
	)
);

adnan on 1/27/10

adnan: Have you tried removing the /* on the prodslug route, or putting the prodslug route before the /:catslug/:subcatslug/* route?

mark story on 1/27/10

Hi, I know this is not a support forum, but as you are writing about routes, maybe it is anyway good place to ask;)
How to do case insensitive routes?

If I have similar scenario:
Router::connect(’/here’, array(‘controller’=>‘pages’, ‘action’ => ‘category’, ‘slug’ => ‘here’), array(‘pass’ => array(‘slug’)));
Router::connect(’/there’, array(‘controller’=>‘pages’, ‘action’ => ‘category’, ‘slug’ => ‘there’), array(‘pass’ => array(‘slug’)));

So that slug=Here or slug=HeRe will point still to the same page as slug=here? I found notes about possibility /(?i)here but it’s shown in the URL… Is there any hidden parameter or trick that I am missing?

Thanks a lot for help:) Luk

Luk on 1/21/11

Hi,

Perhaps you could shed some light on this?

I’m attempting to connect:

site.com/hotels
to this:
site.com/contents/listings/category:2

I tried:

Router::connectNamed(array(‘category’), array(‘default’ => true)); Router::connect(’/hotels’, array(‘controller’ => ‘contents’, ‘action’ => ‘listings’, ‘category’ => 2));

But this seems to break my routing. Any idea how to (correctly) use named parameters with custom routing?

Thanks.

Dave on 6/18/11

Hi mark,

First of all, thanks man, you’ve a done and continue doing a great job.

Now I have two question, the first, I want to add a trailing slash to all the links, I’ve read about, and I’ve achieved it the way explained in this post ,but as this post is from 2008 I wonder if now there is a way to do it without having to overwrite the url’s helper function. That’s only if you know about it, if not there is no problem, that solution is ok for me.

The second question is if there is a way to have optional parameters in routed url, I mean, if I can route optional parameters this way:

Router::connect('/:country/obras/:filtro/', array('controller' => 'obras', 'action' => 'index'), array( 'pass' => array('filtro'), 'filtro' => 'todas|finalizadas|en_construccion', 'country' => '[a-z]{3}' ));

In this url, I need y to be optional, so for example:
domain.com/obras/todas work as well as domain.com/arg/obras/todas does.

How may I achieve this? is this considered in cakephp routing?

Thanks in advance.

Javier on 8/3/11

Hey mark,

I want in creating route which will render same views but links will be different.

For example

I have a link products/list and I want it to change when user came on that page from browse tab and it will become

browse/products/list

and rest of other links will also act as
browse/xyx/index
or
browse/xyx/edit/1
etc

Please help!

Edris

Edris on 1/27/12

Edris: Sounds like you might get hit with duplicate content penalties in doing that. The easiest way to do what you want is have 2 controller methods, and just used setAction() so you don’t duplicate the code.

mark story on 1/28/12

Hello Mark,

Thanks for your quick reply.

I test setAction and it does the partial job, I also need links to be changed if user comes from browse sections. Let say:

I have a link products/index and there is a button called Edit which linked to products/edit/1

So if user access products from browse/products/index

all other links on that page should be changed automatically, and Edit button link will become browse/products/edit/1

I am afraid is this possible in cakePHP through configuring routes?

-Thanks again
-Edris

Edris on 1/30/12

Comments are not open at this time.