Mini Controllers - modular page design

28 February, 2007

Show comments

The MVC pattern adds some clarity to what should go where when it comes to designing a page which requires some data from a data source. The model gets the data from the data source; the controller might do something with that data and then the view will display what the controller passes to it. If the page handles only a single function that's rather easy to both conceptualize and do. However many pages don't just do a single thing, and are made up of modules which may be user customizable and it is this I wish to discuss here. To give a none-cake example of what I am referring to, take a look at the google personalized home page and consider how in cake you would build a page which allows users to select and put various 'blobs' onto their page, and how it would be initially rendered. I choose the term 'blobs' in an attempt to avoid ambiguity with cake terminology.

So use requestAction§

Probably the immediate thought would be to use requestAction which is mentioned in the manual and of course in the API. RequestAction is a very useful function that (amongst other things) allows you to insert the result of a different url into the current page, and is a logical choice for such a requirement, and it's what I did in the past (until the recent [site update][Site update version 3]). It's fast and easy to do and gets the job done.

When not to use requestAction§

The trouble with requestAction is it's not free. This becomes apparent if, as I did whilst experimenting in the past, you think "That's great, I´ll use if whenever I want something from a different view". As soon as you include more than 1 or 2 reqeustAction calls you may notice that the time to render (page + xreqeustAction calls) is comparable to the time to render ((1+x)normal page requests). The render time is less than the equivalent number of normal pages requests but it means that, considering the google example I mentioned above, it would not make a viable solution - if you include 10 blobs on the page your page is soaking up ~10 times what it should from the server. I will not delve into the misuse of requestAction to call logic that is in a controller that should be in a model etc. :).

Enter Mini Controllers§

Previously I made use of vast quantities of requestAction with the code for this blog to retrieve and insert my code snippets into posts as they were requested, and also for the tag cloud etc. Whilst fiddling around I came up with the following thought: "What if certain wholly self-sufficient components which can be swapped in and out are used to get the data, and some bullet proof logic is applied in the view/layout/an element to process this data?". In the case of the google home page this would apply to the entire view (with the exception of the search form); in my case this view logic is the side menu (perhaps obvious).

So using what I know best (my own code): For each menu block there is an component which retrieves and passes it's menu data to the view, and the side menu element cycles through the menu data using some funky logic to check for a specific element to render or whether to use a standard menu element. In this post I will focus only on the component side of the logic.

Configuring what appears in the side menu basically means only including or not the component in the controller, as if the component is removed, it's data isn't passed to the view and the side menu element will not add this 'blob' to the final rendered page. A bonus when it comes to debugging, remove the component and it's influence disappears.

How does that work?§

Below is the base component class that is used for this purpose. It is never instanciated and is my pseudo-appComponent class. At startup if the method _continue returns true, the component will automatically go find and set it's data to the context menu variable (incidentally there's also a test included to see if the side menu is completely disabled, which is activated making use of a [Change the config at run time][]). If the data isn't available at startup (such as with related articles, whereby you need to know the contents of the article before you can find what's related to it) the component can be disabled by default and set in motion by calling the method process explicitly from the controller once the info is available.

Below is a simple example of a mini-controller. The important thing to note is that only the _getData method has been overridden. Handling setting the data, and even data caching to avoid needless queries to the data source is handled by the base component class.

Should a new menu item be required, a new component is created extending the template class and the _getData method overridden to retrieve the necessary data for display.

Wrapping Up§

Presented here is an idea of how to collect and present modular page info in a scalable manner. It breaks some rules (namely: don't manipulate a parent from a child object; don't access models in components), however the results are flexibility with good render times compared to alternatives. I didn't address the presentation logic in this post, lodge your interest if you'd like to see that :).

Bake On!