Archive for May, 2009

CakePHP: Prettier URLs for Plugins

Posted in CakePHP on May 28th, 2009 by Jamie – 2 Comments

Well, I’m oficially a CakePHP convert – so much so that I’m in the middle of writing a general purpose CMS based on the excellent framework (I’ll probably convert this blog to Cake if I ever get the time!). One of the most convenient aspects of Cake is its plugin feature, which allows for easy code drop-in from project to project (a feature that my CMS is based on). One shortcoming, however, is that – in an Interwebs world full of ‘pretty’ URLs – plugin URLs are unncessarily long, since by default they include the plugin name as well as the controller and action. I would much rather get rid of the plugin name in the URL and just have the Router figure things out, you know, automagically.

For example, if we have a “calendar” plugin with a “CalendarEvents” controller that has a “view” method, the URL will look something like this:

http://mydomain.com/calendar/CalendarEvents/view

That’s not terrible (certainly better than ?plugin=calendar&page=events&action=view blahblah), but a little lengthy for my liking. We could just rename CalendarEventsController to EventsController, but Events is a pretty general controller name so we could have a future conflict. It’s always best to prefix any plugin controller names with the name of the plugin itself.

So what to do? I’d love to have this:

http://mydomain.com/calendar/events/view

More intuitive and concise, I think. And, since I couldn’t find anything in the Cookbook, Bakery, Forge or Google group that implemented this to my liking, I gave it a go myself. The process is actually quite simple, and only involves editing a couple of files.

The first thing we need to do is find a way to gather up all of our plugin controllers. Tucked away in one of the Bakery’s many tutorials, I found a method called, aptly enough, “_get_plugin_controller_names()”. The tutorial on the Bakery puts this method inside of the AppController, but I’m going to put it in app/config/bootstrap. I’ve also modified it slightly so that instead of returning a flat list, it returns a multi-level array organized by plugin. Here’s the code:

/**
 * Get the names of plugin controllers
 * Adapted from: http://book.cakephp.org/view/647/An-Automated-tool-for-creating-ACOs
 * /app/config/boostrap.php
 *
 * This function will get an array of the plugin controller names, and
 * also makes sure the controllers are available for us to get the
 * method names by doing an App::import for each plugin controller.
 *
 * @return array of plugin names.
 *
 */
function _get_plugin_controller_names(){
    App::import('Core', 'File', 'Folder');
    $paths = Configure::getInstance();
    $folder =& new Folder();
    // Change directory to the plugins
    $folder->cd(APP.'plugins');
    // Get a list of the files that have a file name that ends
    // with controller.php
    $files = $folder->findRecursive('.*_controller.php');
    // Get the list of plugins
    $Plugins = Configure::listObjects('plugin');

    // Loop through the controllers we found in the plugins directory
    $names = array();
    foreach($files as $f => $fileName)
    {
        // Get the base file name
        $file = basename($fileName);

        // Get the controller name
        $file = Inflector::camelize(substr($file, 0, strlen($file)-strlen('_controller.php')));

        // Loop through the plugins
        foreach($Plugins as $pluginName){
            if (preg_match('/^'.$pluginName.'/', $file)){
                // First get rid of the App controller for the plugin
                // We do this because the app controller is never called
                // directly ...
                if (preg_match('/^'.$pluginName.'App/', $file)){
                    unset($files[$f]);
                } else {
                                if (!App::import('Controller', $pluginName.'.'.$file))
                                {
                                    debug('Error importing '.$file.' for plugin '.$pluginName);
                                }

                    /// Now prepend the Plugin name ...
                    // This is required to allow us to fetch the method names.
                    $names[$pluginName][] = $file;
                }
                break;
            }
        }
    }

    return $names;
}

When called, it’ll look something like this:

$plugins = _get_plugin_controller_names();
debug($plugins);

Array
(
    [Calendar] => Array
        (
            [0] => CalendarEvents
        )

    [Faq] => Array
        (
            [0] => FaqFaqs
        )

)
[/sourcecode]

Great! Now we just need to put it in our router and add some code to interpret the findings. So, somewhere in /app/config/routes.php (before the last catch-all route of course), add something like this: 
foreach ($pluginControllers as $plugin => $controllers) {
	foreach ($controllers as $controller) {
		if (substr($controller, 0, strlen($plugin)) == $plugin) {
			$controller = substr($controller, strlen($plugin));

			Router::connect('/(?i)'.$plugin.'/'.$controller.'/:action/*', array('plugin' => $plugin, 'controller' => $plugin.'_'.$controller));
			Router::connect('/(?i)admin/'.$plugin.'/'.$controller.'/:action/*', array('plugin' => $plugin, 'controller' => $plugin.'_'.$controller, 'admin' => true));
		}
	}
}

Basically, what I'm doing is looping through the array of plugins, and, within each plugin, looping through the array of controllers. I do a check to make sure the controller name is prefixed with the plugin name (so "CalendarEvents" is in the "Calendar" plugin), and, if so, I remove the plugin name from the name of the controller and pass along all of the information to the Router. I've also thrown in an extra line to ensure that my admin actions are properly routed.

And that's it! I'm not sure if it's the ideal solution, or if it's 'Cakey' enough for the established users out there, but it works for me and is simple enough. I'd love to hear comments, improvements, etc.

Blogging takes too much energy

Posted in Personal on May 19th, 2009 by Jamie – Be the first to comment

It’s official. How do people find the time to go to a day job, do side projects, spend time with the family, relax away from the computer, AND update a blog? I’ve got so many posts in draft that have been sitting unfinished for weeks or months, and I don’t know when I’ll find the time to get to any of them. Whew. Stay tuned… I guess? Catch me on Twitter for semi-regular updates.

Your Splash Page Sucks

Posted in Opinion on May 5th, 2009 by Jamie – 1 Comment

You heard me – it sucks! I’m especially looking at you, Flash developers.

To be fair, yours isn’t the only one that sucks – all splash pages (enter gross generalization here) suck. The internet is about content, and splash pages, especially those containing the ubiquitous and oh-so-irrelevant ‘Flash introduction’.

The more clicking a user has to do to get to the content he’s looking for (or the content you want him to find), the less likely he is to actually get there. If I’m shopping for an item and I can buy it at two stores, but one of the stores has three doors blocking the item and the other store has no doors at all, which store am I likely to enter? Common sense would dictate that I would enter the store that’s easier to navigate and has less barriers between me and the content I want.

So why, then, are Flash splash pages so popular? The number of ‘professional’ websites that have useless splash pages – often in a slow-loading medium such as Flash – astounds me. Clients usually ask for splash pages because it gives them a chance to show off, either that they have a big budget or that they like to be f(F)lashy. But what these clients don’t realize is that a splash page can often trip up search engine spider bots, since it represents a barrier between the spider and the real content. The worst case scenario is that the spider will index only the splash page, leaving your content in the dark.

Splash pages are usually self-indulgent and extraneous. Moreover, those who push for their inclusion on a website usually don’t understand the internet as a medium. Flash/splash advocates usually have too much money and not enough brains, so they just throw a whole bunch of cash at a company because they want something that looks ‘cool’ or ‘flashy’ or ‘impressive’ or whatever; usability and content definitely are not priorities. The minority of splash pages that do have content could probably be retooled so that the content is just on the ‘main’ homepage of the website. Jared Spool, who is with User Interface at Macromedia, is quoted on MarketingSherpa.com as saying:

When we have clients who are thinking about Flash splash pages, we tell them to go to their local supermarket and bring a mime with them. Have the mime stand in front of the supermarket, and, as each customer tries to enter, do a little show that lasts two minutes, welcoming them to the supermarket and trying to explain the bread is on aisle six and milk is on sale today. (taken from SEOmoz)

Could you imagine that? I know I’d last about six seconds before either punching the mime in the face or going to a different store. And, in website terms, since you can’t usually punch site owners in the face, then you’ll probably just go somewhere else for the same information.

Jakob Nielson, who would get my vote for the President of the Internet, lists Flash – that fixture of bad splash pages – as one of the Top Ten Web Design Mistakes  of 2005 (all of which still apply today). Nielson writes that Flash intro pages are “so bad that even the most clueless Web designers won’t recommend them, even though a few (even more clueless) clients continue to request them”. Smashing Magazine has an excellent showcase of splash pages, from the very worst to the poorly designed to the not-so-intolerable-but-still-unnecessary. Anytime I come across a splash page – especially one done in Flash – I turn around and walk my virtual self out the door.

I’ll end with another golden Nielson quote from the same article as above – some advice on how to design your website so that you don’t need Flash, splash, or anything of the sort:

Flash should not be used to jazz up a page. If your content is boring, rewrite text to make it more compelling and hire a professional photographer to shoot better photos. Don’t make your pages move. It doesn’t increase users’ attention, it drives them away; most people equate animated content with useless content.