Custom URLs from the Site Root
A number of sites nowadays are offering custom, unique URLs for people. I'm sure we can all think of one small social networking site that does this. I had a need to do this within a new Cake app I'm writing. Plus, since I'm migrating from an existing site, this is a legacy feature that must be supported.
A number of sites nowadays are offering custom, unique URLs for people. I'm sure we can all think of one small social networking site that does this. I had a need to do this within a new Cake app I'm writing. Plus, since I'm migrating from an existing site, this is a legacy feature that must be supported. I had to find a way to make http://www.example.com/username route to http://www.example.com/users/view/username.
I will say that I don't know if this is the best solution, but it is the one I currently have working. It does require you to do some extra work whenever you're creating new controllers, but the work is minimal. Also, this tutorial will only allow one of your controllers to have this functionality.
All these changes take place inside of cake/app/config/routes.php. For this example, we'll assume you have a collection of users and your typical URL for viewing a user is http://www.example.com/users/view/username. Let's also assume you have two other controllers named items and images. Finally, this assumes you have a page called error, accessible at /pages/error.
First off, we need to create the route for our custom urls. The route you would need to use is as follows:
What this route says is that for anything that comes in, you will use the users controller and the view action for that controller, essentially making the entire url a list of parameters.
We have to be careful where this goes in routes.php, as this is basically a catch all for any URL given. We have to be sure this is the last route used:
As you can see, the two default routes come first with our new route coming last.
We're not finished here yet. Since we now have this catch all in place, we need to ensure that all our controllers are treated correctly. To do so for our controllers, we add the following to routes.php:
Because of our new route, anything not matching the base path route or the pages route will be routed through the view action of the users controller. We need to ensure we grab URLs that should maintain Cake's default routing.
The final step in this process would be to do some error handling. Since we're using our catch all, anything that doesn't exist is still going to try to route to our users controller. A quick bit of work on the view function of our controller will redirect the user if an invalid URL is given:
If we can find the username given, $user will be set and we can then set 'user' in our view. If not, redirect them to an error page.
That's all there is to it. I'm sure there are more ways to tweak this strategy, but for now, it seems to work pretty well.
I will say that I don't know if this is the best solution, but it is the one I currently have working. It does require you to do some extra work whenever you're creating new controllers, but the work is minimal. Also, this tutorial will only allow one of your controllers to have this functionality.
All these changes take place inside of cake/app/config/routes.php. For this example, we'll assume you have a collection of users and your typical URL for viewing a user is http://www.example.com/users/view/username. Let's also assume you have two other controllers named items and images. Finally, this assumes you have a page called error, accessible at /pages/error.
First off, we need to create the route for our custom urls. The route you would need to use is as follows:
$Route->connect('/*', array('controller' => 'users', 'action' => 'view'));
What this route says is that for anything that comes in, you will use the users controller and the view action for that controller, essentially making the entire url a list of parameters.
We have to be careful where this goes in routes.php, as this is basically a catch all for any URL given. We have to be sure this is the last route used:
<?php
// cake/app/config/routes.php
// Default route
$Route->connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
// Default pages route
$Route->connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
// Custom URL route
$Route->connect('/*', array('controller' => 'users', 'action' => 'view'));
?>
As you can see, the two default routes come first with our new route coming last.
We're not finished here yet. Since we now have this catch all in place, we need to ensure that all our controllers are treated correctly. To do so for our controllers, we add the following to routes.php:
<?php
// cake/app/config/routes.php
// Default route
$Route->connect('/', array('controller' => 'pages', 'action' => 'display', 'home'));
// Set default controller routes
$Route->connect('/users/:action/*', array('controller' => 'users'));
$Route->connect('/items/:action/*', array('controller' => 'items'));
$Route->connect('/images/:action/*', array('controller' => 'images'));
// Default pages route
$Route->connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));
// Custom URL route
$Route->connect('/*', array('controller' => 'users', 'action' => 'view'));
?>
Because of our new route, anything not matching the base path route or the pages route will be routed through the view action of the users controller. We need to ensure we grab URLs that should maintain Cake's default routing.
The final step in this process would be to do some error handling. Since we're using our catch all, anything that doesn't exist is still going to try to route to our users controller. A quick bit of work on the view function of our controller will redirect the user if an invalid URL is given:
function view($username)
{
if ($user = $this->User->findByUsername($username)) {
$this->set('user', $user);
} else {
$this->redirect('/pages/error');
exit();
}
}
If we can find the username given, $user will be set and we can then set 'user' in our view. If not, redirect them to an error page.
That's all there is to it. I'm sure there are more ways to tweak this strategy, but for now, it seems to work pretty well.








So I use word boundaries \b to match the exact word.
(?!\badmin\b|\bitems\b|\bimages\b)(.*)
I actually wanted a Route that is dynamic. Everything from
http://www.example.org/* should be routed to one controller (users) EXCEPT every URL that starts with a existing controller! And this controller should match EXACTLY (with lowercase and uppercase first character). So heres my full code:
//get all controllers
$Configure = &Configure::getInstance();
$controllerList = $Configure->listObjects('controller');
//add prefixes (neccessary because admin might just be prefix and not controller)
$controllerList[] = 'Admin';
$controllers = '\b'.implode('\b|\b', $controllerList);
$controllers .= '\b|\b'.low(implode('\b|\b', $controllerList)).'\b';
//print($controllers);
Router::connect('/:pageName/*',
array(
'controller' => 'users',
'action' => 'index'
),
array(
'pageName' => '(?!'.$controllers.')(.*)'
)
);
There could be a bug if your controllername is like pageTests, but for my project it doesn't matter.
I think there's some routing problem in my cake.
This URL below should display an image but it's trying to rendering a view now.
http://www.in-culture.info/fckfiles/image/nw.jpg
Here's my full question
http://stackoverflow.com/questions/3738001/cakephp-is-interrupting-images
Thanks for helping,
MOe
Router::connect('/admin/', array('controller' => 'users', 'action' => 'login', 'prefix' => 'admin'));Just change last line.
Router::connect('/*', array('controller' => 'members', 'action' => 'show'));
to
Router::connect('(?!admin|items|images)(.*)', array('controller' => 'members', 'action' => 'show'));
Took about a week looking for information on how to customize the URL from the paginate method.
I found several things but nothing really useful, and the truth that I am a little off and I saw that you work with CakePHP.
The issue is that I have to keep the urls of my site just because they are indexed in Google and the SEO and the structure of URLs is very well composed. For example, I have a url like:
http:miproyecto.loc/padres
which I included in a routes.php enroute to do the following:
Router::connect ('/padres', array('controller'=>'contenidos', 'action'=>'listado','parameters'=>'listar_todo'));
and when to call the method paginate:
$array_contenidos = $this->paginate('Contenido',$criterios);
the url that the paginate method looks like:
http://miproyecto.loc/contenidos/listado/page:2
but I want to get
http:miproyecto.loc/padres-2.
I tried everything and I managed to change the urls generated by the method of pagination In the function link of the helper paginator.php I have included the following lines:
// new url code
$seccion_url = explode("-",$this->params['url']['url']);
$url = 'http://'.DOMAIM.'/'.$seccion_url[0].'-'.$url['page'];
return $this->{$obj}->link($title, $url, $options);
But now I have the problem to put in all the rules (manually) routes.php (which I can die because there are thousands)
Router:: connect ( '/ preconception-2', array ( 'controller' => 'content', 'action' => 'list', 'parameters' =>' listar_todo ',' named '=> array (' page '=> 2)));
I hope you can help me and give some advice or instructions.
//get all controllers
$Configure = &Configure::getInstance();
$controllerList = $Configure->listObjects('controller');
foreach($controllerList as $controllerName)
{
//map all controllers (apart from app and pages to their name
if($controllerName != "App" & $controllerName != "Pages")
{
//route the normal name
Router::connect('/' . $controllerName . '/:action/*', array('controller' => $controllerName));
//get the name with first letter lower
$firstLetterLower = strtolower(substr($controllerName,0,1));
$lowerCaseName = $firstLetterLower . substr($controllerName,1);
//route the name with first letter lowered
Router::connect('/' . $lowerCaseName . '/:action/*', array('controller' => $lowerCaseName));
}
}
YESSSSSSS! This is exactly what I was looking for. I had no idea one could retrieve the controllers like that. This piece of code has enabled me to finally reach my goal of not having "/pages/" in the URL and not having to manually route everything.
Thank you very much John!
So
$Route->connect('admin/items/:action/*', array('controller' => 'items', 'admin' => '1'));
will use the $item->admin_:action
amazing!
http://manual.cakephp.org/chapter/configuration section 4
basically you have
// Custom URL route
$Route->connect('/*', array('controller' => 'users', 'action' => 'view'));
then type in an admin url like
www.mysite.com/admin/items/add
this will look for a user admin rather than looking for a action admin_add inside the items controller.
Is there any way to solve this?
One method i though of was just to scrap the define('CAKE_ADMIN', 'admin');
and write admin routes for each controller.
$Route->connect('admin/items/:action/*', array('controller' => 'items'));
I was looking for a different thing which I've got from anton morrison's answer. For my application's admin panel when I browse to the admin panel http://abcoder.com/admin I see an error. In fact I want to show the log in page there which is actually from http://abcoder.com/admin/users/login. I did not know I was missing the 'admin' => '1' part. How do you guys know that much about cakePHP! In the whole manual there was no 'admin' => '1' ! :(
This is what I used for my need:
Router::connect('/admin/', array('controller' => 'users', 'action' => 'login', 'admin' => '1'));Thank you anton morrison.
$Route->connect('/~(.*)', array('controller' => 'users', 'action' => 'view', '$1'));
to accept urls like http://www.example.com/~username
$Route->connect ('(?!admin|items|images)(.*)', array('controller'=>'users', 'action'=>'view'));
It can be translated like this: when it doesn't have to deal with controllers like items or images, or admin panel, then it treats all the urls with controller users, action view.
Wow, thanks Lucian this piece of code is brilliant.
Comments are closed for articles over a year old