3

I've been tasked with rewriting an existing website with large pre-existing link catalog. For argument's sake, let's assume we can't do anything that would change the link catalog. Here's a few examples of the link structure we're working with:

  1. An item page would be:

    www.domain.com/widgets/some-totally-awesome-large-purple-widget
    
  2. A category sub page page would be:

    www.domain.com/widgets/purple-widgets
    
  3. A category parent page page would be:

    www.domain.com/widgets/
    
  4. A custom page may be:

    www.domain.com/some-random-page
    

The various page types are too numerous to write individual Routers for.

Using Router::connect I can easily account for the first and second scenarios using something like:

Router::connect('/{:pageroot}/{:pagekey}', 'Pages::index');

In turn, the Pages::index method looks for entries in our database with the "key" of '/widgets/purple-widgets'.

However, the framework defaults to the '/{:controller}/{:action}/{:args}' route for pages like the third and fourth. I know that this is the correct behavior for the framework. Also, best practice would state that I should write the site to match this behavior. But, that isn't an option here.

What I need is a Router that would allow the third and fourth examples to function the same as the first. All examples should be sent to the Pages::index controller, which in turn queries a database using the URL path as a key.

Eric C
  • 971
  • 6
  • 14

2 Answers2

2

If you don't have any convention in the URL for what is what, between page, item and category. I'd go with a very generic router.

Router::connect('/{:category}/{:page}/{:item}', 'Pages::any');
Router::connect('/{:category}/{:page}', array('Pages::any', 'item' => null));
Router::connect('/{:category}', array('Pages::any', 'page' => null, 'item' => null));

And in Pages::any() to search for the correct stuff. Is that category a page after all (example 4)? Is that page an item (example 1)?

or

You store the URL somewhere (e.g. a mapping table in the database) and use the pattern version of a lithium Route.

Router::connect(new Route(array(
    'pattern' => '@^/(?<path>.+)$@',
    'params' => array('controller' => 'pages', 'action' => 'any'),
    'keys' => array('path' => 'path'),
    // extra stuff, if the path is `tata`, it skips this route and uses
    // any of the following ones that matches.
    'handler' => function($request) {
        if ($request->params['path'] == 'tata') {
            return false;
        } else {
            return $request;
        }
    }
)));

From that point, you'll get the full URL.

greut
  • 4,305
  • 1
  • 30
  • 49
  • I like specifying a custom Route() but I couldn't get it to work; I'll toy with that later. The first solution, with some tweaking, worked great. I forgot you could specify an array of $params for Router::connect(); – Eric C Jan 04 '12 at 14:43
  • @EricC the crucial `keys` parameter was missing, it does the matching between the regexp and the `Request::$params` your controller receives. – greut Jan 04 '12 at 16:29
1

You probably should write a smart Router Helper which is maybe able to process your request based on your db defined routes.

Take a look into: net/http/Router.php

especially connect(), parse() and match()

I would start to write some kind of anonymous function and progress it to a testable Class which is located in /extension.. ?

dgAlien
  • 428
  • 1
  • 4
  • 9