tags:

views:

141

answers:

1

So we have a real-estate-related site that has controller/action pairs like "homes/view", "realtors/edit", and so forth. From on high it has been deemed a good idea to refactor the site so that URLS are now in the format "/realtorname/homes/view/id", and perhaps also "/admin/homes/view/id" and/or "/region/..."

As a mere CakePHP novice I'm finding it difficult to achieve this in routes.php. I can do the likes of:

Router::connect('/:filter/h/:id', array('controller'=>'homes','action'=>'view'));
Router::connect('/admin/:controller/:action/:id');

But I'm finding that the id is no longer being passed simply and elegantly to the actions, now that controller and action do not directly follow the domain. Therefore, questions:

  • Is it a stupid idea to play fast and loose with the /controller/action format in this way?
  • Is there a better way of stating these routes so that things don't break egregiously?
  • Would we be better off going back to subdomains (the initial method of achieving this type of functionality, shot down on potentially spurious SEO-related grounds)?

Many thanks for any advice! I'm sorry that I'm such a newbie that I don't know whether I'm asking stupid questions or not....

A: 

There's nothing particular exotic about the routes you're trying to build. Without a description of exactly how your routes/dispatches are breaking, it's hard to identify any esoteric routing behaviours you might be running into (and believe me, there are some).

Before getting to specifics, I will recommend strongly that you always use reverse-routing to build your URLs (ie. $html->url(array('controller'=>'homes','action'=>'index')); instead of $html->url('/homes/index'). It's a small performance hit that will eventually save you from some massive headaches.

Let's run through your example routes, and I'll try to explain how to build a matching URL in your views, and highlight potential issues you're running into.

Router::connect('/:filter/h/:id', array('controller'=>'homes','action'=>'view'));

You would build a URL in your view with the following reverse-route:

$html->url( array(
    'controller'=>'homes','action'=>'view',
    'id'=>$some_id,'filter'=>$some_filter
));

Note that I've passed in 'id' and 'filter' keys, instead of just passing the corresponding variables. This is because Cake treats reverse-route parameters without keys in order. So if you called:

$some_filter = 'foobar';
$some_id = 1234;
$html->url( array('controller'=>'homes','action'=>'view',$some_id,$some_filter));

You'd end up with this URL:

/1234/h/foobar

Which is backwards.

The other issue you may be running into is how this route gets dispatched to a controller action. By default, Cake will pass all parameters of the route request in the order they appear in the route definition, unless instructed otherwise. So, with the route above, Cake would call:

HomesController::view( $filter, $id );

If you want the first parameter to HomesController::view to be $id, you will need to specify such in the route definition:

// This route definition...
Router::connect(
    '/:filter/h/:id',
    array( 'controller'=>'homes', 'action'=>'view' ),
    array(
        'pass' => array( 'id', 'filter' )
    )
);
// ...invokes the controller action:
// HomesController::view( $id, $filter );

The 'pass' element of the third array parameter to Router::connect tells Cake which route parameters, and in what order, to pass to the controller action. You could remove 'filter' from the array, and Cake would invoke the action thus:

HomesController::view( $id );

In this case, Cake still captures $filter and makes it available to the controller, as an element of the Controller::params property. You could access its value by addressing $this->params['filter'].


The second route you provided is:

Router::connect('/admin/:controller/:action/:id');

For admin routing, you don't actually need to define specific routes preceded by 'admin'. You only need to turn on admin routing. In your /app/config/core.php, look for and uncomment the line:

Configure::write('Routing.admin', 'admin');

This allows you to define a set of actions with the admin_ prefix (e.g. admin_edit, admin_publish, etc.), which are dispatched-to by a URL like /admin/homes/edit/1234. In your views, you would structure your reverse-routes like so:

$html->url( array(
    'controller'=>'homes','action'=>'edit',1234
    'admin'=>true
));

Note the 'admin'=>true part, which informs Cake you're requesting an admin-prefixed route.

Daniel Wright
This is a wonderfully thorough reply which has definitely set me back on the right track, so thank you! One question, is it possible to send parameters back in the pass array that are not derived from the URL, e.g. 'pass'=>array('filter', 12345, true)? I'm not having any success with such an approach but it's not entirely clear to me why...
thesunneversets