Search code examples
phprefactoringurl-routinglithium

Advice on Refactoring Custom Routing Scenario (Lithium Framework)


A continuation from previous question: Custom lithium routing scenario

Note: This is specific to the Lithium Framework

The Problem

I've inherited a URL scheme that doesn't really have any convention to differentiate between pages, items and categories. So, I've got a very generic router that passes to a catch-all controller.

This catch-all controller (PagesController) uses the url as key to retrieve Page Type from a database. The PagesController then runs methods and chooses templates based on a Page Type. I store this information in Memcached indefinitely, so lookups are quite fast.

However, as more page types come into play, I can see this controller becoming too bloated and inflexible. Ideally, I would like to break the different page types out into their own controllers.

The Solution?

Would it be possible to have a routing scenario that checks the database to determine the correct controller?

My first thought is to subclass lithium\net\http\Router and use custom logic in Router::connect() and Router::_parseController().

It would be nice if could query the database in bootstrap\routes.php and create a new lithium\net\http\Route object based on the results. Then, simply pass this to Router::connect(). This seems like an awful hack.

Either way, Router::connect() in it's design isn't meant to be that dynamic.


Solution

  • It's hard to be very specific, without seeing more examples of different page types and other example URL (I did look at the previous question, but I get the sense that that doesn't give the whole picture), but as with the previous question, it sounds like the answer will once again be custom routing with a handler.

    Router::connect('/{:pageType}/{:pageKey}', ['pageKey' => null], function($req) {
        $types = ['list', 'of', 'page', 'types'];
        $controller = 'pages';
        $action = 'view';
    
        if (in_array($req->pageType, $types)) {
            // It's a proper type, do a database lookup and set
            // `$controller` accordingly
    
            // Here I'm assuming category vs. item view for the action:
            $action = $req->pageKey ? 'view' : 'index';
        } elseif (!$req->pageKey) {
            // It's a `/custom` URL, figure out what to do with it
        }
    
        // This lets you return custom, arbitrary parameters that Lithium
        // will use for dispatch
        return compact('controller', 'action') + $req->params;
    });
    

    As you can see, with handlers, you can do pretty much whatever you want. Parse the parameters, run your database calls, and pass back a completely arbitrary set of routing parameters for Lithium to dispatch.

    The only other thing you might want to add to the above is a separate class that can manage the pairings between page types/custom pages, and routing parameters, if your rules start getting complicated.