debuggable

 
Contact Us
 

URL Aliases for CakePHP

Posted on 17/5/06 by Felix Geisendörfer

Deprecated post

The authors of this post have marked it as deprecated. This means the information displayed is most likely outdated, inaccurate, boring or a combination of all three.

Policy: We never delete deprecated posts, but they are not listed in our categories or show up in the search anymore.

Comments: You can continue to leave comments on this post, but please consult Google or our search first if you want to get an answer ; ).

We all know that cakephp wants us to structure all our urls in the "/controller/action/params" (or "/plugin/controller/action/params") way, but sometimes you want domain.com/product or something else that would break that structure. Now you can use routes for that, but what is, if you want to create those aliases dynamically? You could dynamically create a new routes.php, but that's a rather messy approach. A better one is to do some URL rewriting yourself. Here is the solution I came up with:

Where to download

I put the code used as a snipped on CakeForge: Get it here!

How to implement

I created a little php script to put in the vendor folder. Then you can edit your routes.php and rewrite controllerMissing errors to your own Controller.

vendor('CakeUrlAlias');
CakeUrlAlias_Activate($from_url, 'pages', 'display');

The example above would rewrite all controllerMissing errors to your pages controller. Inside this controller you would check if there is a content matching the requested page and if not, trigger the missingController error:

function __triggerMissingController($controllerName)    
{
    return $this->cakeError('missingController', array(array('className' => Inflector::camelize($controllerName."Controller"),
                                                       'webroot'   => $this->webroot,
                                                       'url'       => $this->params['url']['url'],
                                                       'base'      => $this->base)));
}

(when you do this $this->triggerMissingController() you need to pass the name of the $site or whatever that couldn't be found).

I hope this should help some people who are particular about the way their urls should look ; ). It's mostly ment for Search Engine Optimization since Google & Co like keyword density, and a controller name in between domain and page can be bad sometimes.

--Felix Geisendörfer aka the_undefined

 
&nsbp;

You can skip to the end and add a comment.

kashish  said on Oct 12, 2006:

I need something like username.mywebsite.com to be implemented in cakephp.

can you please help me out.

Felix Geisendörfer said on Oct 12, 2006:

kashish: Setting up the sub-domains is not related to CakePHP, but to your hosting setup. Using the sub-domain as a User name can be achieved by using a beforeFilter in your AppController that checks what sub-domain was used to access the site.

James said on Dec 30, 2006:

This seems to do exactly what i need but im fairly new to CakePHP and amn't sure how to implement this with a default Cake set up.

Any chance of a few pointers?

Felix Geisendörfer said on Dec 30, 2006:

James: Well it's been a while since I've written this. These days I would recommend against the method described here and recommend you to have a look at: http://www.thinkingphp.org/2006/09/18/dessert-11-welcome-back-friendly-urls/

James said on Dec 30, 2006:

Cheers Felix, but what im more interested in trying to do is catch when a controller is missing and check if a friendly_url exists in the database. This is why i wanted to catch elements for which the controller didnt exists and direct them at a custom pages controller.

The url would then be used within the controller to determine which page is being viewed.

Felix Geisendörfer said on Dec 31, 2006:

Yes, this is what this post is about. But please don't use this for perma links, you'll run into issues. In order to get it working you have to download the snippet from cake forge put it in /app/vendors and then follow the how to implement steps.

Lorenzo Moretti said on Jul 10, 2007:

Hello!
Your idea is very smart (and CakePHP is really flexible :)!

I think this could be used to localize url for search engine optimization. For example www.domain.com/about_us could be translated, depending on user language, to an other localized url (www.domain.com/chi_siamo in Italian). I will try to develop the url localization idea further. Do you think your approach could be the right one?

Great blog!

Felix Geisendörfer said on Jul 12, 2007:

Lorenzo Moretti: Please read this post of mine: http://www.thinkingphp.org/2006/09/18/dessert-11-welcome-back-friendly-urls/

This is my favorite approach towards SEO-friendly urls as of right now.

balthisar  said on Oct 18, 2007:

This is FANTASTIC! Below are a couple of implementation notes for others that may run into trouble. I just want to comment first that you shouldn't abandon this. This is a perfect solution for static pages when you don't want them buried a level down in your URL.

So, here's how I got it to work for me in 1.1.17.5612:

1. Put the linked-to snippet into /app/vendors/CakeUrlAlias.php

2. In your routes.php, make sure you have the following before any of the routing:
vendor('CakeUrlAlias');

CakeUrlAlias_Activate($from_url, 'pages', 'display');

3. Also in your routes.php, make sure you have the following:
$Route->connect('/pages/display/*', array('controller' => 'pages', 'action' => 'display'));

$Route->connect('/pages/*', array('controller' => 'pages', 'action' => 'display'));

Note that the SECOND line is already there. Make sure you insert the first line in the given order.

4. COPY your cake/libs/controller/components/pages_controller.php into app/controllers
There are two reasons for this -- CakeUrlAlias.php only recognizes controllers in this location, plus we want to make a couple of changes and you shouldn't touch the distribution code anyway.

5. Paste the __triggerMissingController function from above into your new, personal pages_controller.php file.

6. Add this code:
# if the page doesn't exist, trigger MISSING CONTROLLER rather than view not found.

if (!file_exists(VIEWS . 'pages/' . join('/', $path) . $this->ext)) {

$this->__triggerMissingController($page);

}

right before this line:

$this->render(join('/', $path));

near the bottom of the display function in your pages_controller.php.

Now, you can access the static page called test.thtml as such:
/test

/pages/test

/pages/display/test

Root works:
/

Also "subpages" (directories) work:
/mydir/test

/mydir/pages/test

/mydir/pages/display/test

Missing controllers will work properly.
The only negative affect is even if you use /pages/* URL's, page-not-found errors will be displayed as missing controller errors. No big deal, because as soon as you go live (debug to 0), you'll get the normal 404 page.

I like this approach because (a) I get my static pages at root level; (b) it doesn't break the default route for pages; and (c) is allows "proper" controller/function/parameters use of the controller.

Eldz  said on Feb 14, 2008:

I was having some problems with the solution presented above when using explicitly routed URLs. For example, I have the following lines in the routes.php file:

$Route->connect('/layouts/*', array('controller' => 'themes', 'action' => 'index'));

Whenever I try to access mysite/layouts, I get a Missing Controller error. So I thought of adding another parameter in the CakeUrlAlias_Activate function where I could pass the $Route->routes array. Then instead of placing these lines at the start of routes.php, I placed them last:

vendor('CakeUrlAlias');
CakeUrlAlias_Activate($from_url, 'pages', 'display', 'home', $Route->routes);

I then modified the CakeUrlAlias.php snippet's CakeUrlAlias_Activates function into something like this:

// I've added the additional parameter $routes
function CakeUrlAlias_Activate(&$url, $controller = 'pages', $action = 'display', $home = 'home', $routes = null)

{

$parts = explode('/', $url);

if (empty($parts) || empty($parts[0]))
{

$url = $controller.'/'.$action.'/'.$home;

return;

}

$pluginOrController = array_shift($parts);

// Then I've also modified the function for checking if the controller exists
if (CakeUrlAlias_ControllerExists($pluginOrController, $routes) || CakeUrlAlias_PluginExists($pluginOrController))

return;

if (!empty($parts))
$additional = '/'.join('/', $parts);

else

$additional = null;

$url = $controller.'/'.$action.'/'.$pluginOrController.$additional;
}

function CakeUrlAlias_ControllerExists($name, $routes = null)
{

$paths = Configure::getInstance();

foreach ($paths->controllerPaths as $path)
{

if(file_exists($path.$name.'_controller.php'))

{

return true;

}

}

// Check if there is an explicit declaration in the Routes configuration
foreach ($routes as $path) {

if (ereg($name, $path[0])) {

return true;

}

}

return false;
}

That's all. I hope this would prove to be a good additional information for 'ya all. Enjoy!

This post is too old. We do not allow comments here anymore in order to fight spam. If you have real feedback or questions for the post, please contact us.