Woe Is MVC
I have recently been developing a new web site and automated processing system for Aussie Interconnect. After looking around at various frameworks for PHP (CakePHP, Symfony, etc) I settled on Code Igniter. I decided to go with CI because of it's simplicity, adequate documentation and high-quality code. There's just one thing that I ran into recently that has caused me a bit of consternation and is the subject of this post.
The scenario here is that I wanted to develop a dynamic menu for the main site. Originally I had thought to have the menu automatically generated from the available pages from each controller, where each controller would be a top-level menu item. This obviously wouldn't work as most things are just pages of information but not arranged as sub-pages of a main "Page" controller – you want top-level items such as Legals, Services, Employment, etc.
Then, in a very devious programmer way, I had a brain wave: let the site admin set up and manage the menu! They could add in titles, URLs and order the menu items however they like. Apart from being easier to code, it also allows the admin to make a static information page a sub-menu item of dynamic entries like Hosting Plans and vice versa. With this new system thought up I went about trying to implement it.
Code Igniter, like most web frameworks, is based upon the Model-View-Controller (MVC) architectural pattern. Very briefly, the system is split up into a Model, which is responsible for data storage and processing, a View, which takes information and formats into HTML pages for display in a user's web browser and a Controller, which is takes user inputs and performs actions based on them – usually by retrieving data from relevant models and forwarding that to relevant views. Each part of an MVC system can be composed of multiple objects – you don't need a single model, for instance.
Now, the problem I had was that I had to get the information for the menu to the views. In MVC the view is "allowed" to communicate with a model directly to retrieve information that it needs. The menu is a prime example of this: a controller shouldn't have to care whether or not a view needs menu data. If a view wants to display a menu then it just asks for the data from the Menu model and displays it. I thought this was a logic way of partitioning the system and reducing the coupling between objects.
Unfortunately, CI has different ideas about what MVC is. There's no provision in CI for accessing models from views. It's of course possible by working around CI, going back to pure PHP, including the model in the view and calling it directly. Going around a framework doesn't seem right to me though. It's also annoying that CI has some very nice methods for loading models (and libraries) and I can't utilise them. I think this stems from the fact that a "view" in CI is really just a template, as you would expect to have in Smarty or the like. From what I've seen, this is the same across other frameworks, such as TurboGears.
I do wonder about this though. Is a view really supposed to be "dumb"? If all the information a view gets must come from a controller then isn't really a 3-Layer architecture and not MVC? My understanding is that templates are only a part of a view, which also includes processing relating to display of information. My problem with the menu is a good example! Does it not make far more sense to have a view request data directly from a model? If the controller is responsible for passing this data to the view then it must, by necessity, know something about the display.
Let's use a more concrete example of this idea. Let's say I have 3 basic views. One is for general pages of information, one is for viewing information for a hosting plan, and the last one is for purchasing or registering for a plan. Obviously,the first two views will need access to the menu. Initially I have the purchasing forms on a blank page and so don't need the menu data. Thus the controller doesn't get that data or pass it on. Later, however, I get feedback that the purchasing forms should be on a similar page to the others – including having a menu. Even though this should only be an issue for the view (it's just layout) I need to change the controller as well to be able to do it.
I realise the example is pretty simple but I think it emphasises that sometimes you really do want to let the view worry about some things itself and not hassle the controller.This has caused me to probe further into the MVC architecture though and I'm interested in doing further research into it. It might be best if I do go around CI and load model data directly with a view.
For the moment though, I am simply going with the flow and forcing the controllers to grab menu data and pass it on to views. At the very least, if I make every controller do this, then a view can choose for itself whether or not to do anything with the data.