In this article we will introduce a powerful new tool in the arsenal of developers - the ViewModel. This provides MVC applications a natural location for presentation logic and lazy functionality while maintaining the segregation between the layers of responsibility within the code. It allows designers access to data and methods they need, while hiding aspects that aren't needed at view level.
The tried-and-tested Model-View-Controller (MVC) pattern has become the mainstay of Web applications development, and numerous PHP frameworks such as Zend, Symfony and Kohana all implement variations on it (Ed: see also the post on Hierarchical MVC we featured recently). Indeed, pretty much any modern Web framework has an understanding of the important segregation of duties inherent within MVC.
It is precisely this segregation that leads to a certain greyness around the all-important View, particularly on sites where multiple content items are displayed in different ways.
To illustrate this, consider the example of a subscription-based website with multiple articles per page. Articles can be displayed either as the main article on the page, or as headlines or teasers linking to the main text. For the purposes of this example, we will use Zend Framework, and assume articles are garnered from a Service API, and turned into Example_Content_Article Models in an action helper used by the Controller. We'll also assume that the helper takes care of any dependency injection requirements of an Article Model from the Bootstrap.
There are a number of ways you can inject information regarding Articles from the Controller into the View, and we will examine these in turn, starting with the simplest case.
Flattening Data
One approach is to take each Article Model, retrieve the title and URI of each article, assemble an array of these values, and then inject that into the View. The controller code would then look something like this:
<?php class FooController extends Zend_Controller_Action { public function indexAction() { $articleLinks = array(); $helper = $this->_helper->article; if ($articles = $helper->getArticles(10)) { // fetch 10 articles foreach ($articles as $article) { $articleLinks[] = array( 'headline' => $article->getHeadline(), 'link' => $article->getUri() ); } } $this->view->articleLinks = $articleLinks; } } |
This is a valid and fairly common approach, and it is adequate in this simple case. Working in this way means that there is no danger of the design team accidentally getting too much power (more on this issue later in the post).
However our simple case becomes more complicated when the visual designs change and require the first article in the column to additionally display its image. We would then amend our article-fetching loop to look like this:
if ($articles = $helper->getArticles(10)) { // fetch 10 articles foreach ($articles as $key => $article) { $params = array('headline' => $article->getHeadline(), 'link' => $article->getUri()); if (0 == $key) { $params['image'] = $article->getImage(0); } $articleLinks[] = $params; } } |
At this point, things are still looking good and the controller isn't getting too cluttered ... until the first two stories need to be displayed with the content from their leading paragraphs showing alongside the title, link and for the first story, their image. So our code again becomes more complicated:
if ($articles = $helper->getArticles(10)) { // fetch 10 articles foreach ($articles as $key => $article) { $params = array( 'headline' => $article->getHeadline(), 'link' => $article->getUri() ); if (0 == $key) { $params['image'] = $article->getImage(0); } if ($key < 2) { $params['standfirst'] = $article->getStandfirst(); } $articleLinks[] = $params; } } |
By now, you may be getting that slight "uh-oh" feeling, as our once very clean Controller is starting to fill up with View-related functionality. You could argue that this is still considered business logic; although it is not the usual access control or workflow-based logic, this functionality is still important to a website that lives by its content.
We can now take our fictitious example one step further. What if the editor wants to colour headlines based on the type of article? Or if on certain templates, the top two stories have images?
The last two considerations are important because, up until this point, we didn't care about the Article type - however now we do, purely based on a design consideration. The second issue we already had something in place for, but it is tedious to have to go back in and change our Controller for one image, especially if the conditional logic for different templates could become very complicated or change often. What about truncation of long headlines? Or bylines?
At this point, the development team generally starts grinding their teeth as the majority of their Controller becomes a portal for presentation logic, as opposed to actual business logic. Shifting the logic out of the Controller and into an Action Helper is merely transplanting a problem - helpers are supposed to be portable code to assist Controllers in business logic, not presentation logic.
A View Helper would be an obvious answer, but for it to be effective, we have to ensure that the View has access to all the information it needs from the Article Model. In the case of something as heavy as an in-depth feature, this could mean loading up ten headlines, images, leading paragraphs and even body copy, just in case the View needs it. Either wave goodbye to design flexibility or performance, your choice.
Injecting Models
Going back to our scenario for a moment, when the number of amendments that are to do with presentation logic becomes frustrating, the development team is left with two choices. Either create a massively complex WYSIWYG-style page-building CMS with individual control of all elements on a page; or give the View direct access to Models. Upgrading the current CMS is discounted on two grounds: firstly, the flexibility provided still would not be flexible enough for the design team (the CMS would need to know in advance a definitive list of all possible considerations in how data changes presentation), and secondly, to develop such a solution would be prohibitively costly in terms of the time and effort needed.
One way to provide the required data is to modify the Controllers to give the View the Models themselves, so that the design team can utilise the data directly:
<?php class FooController extends Zend_Controller_Action { public function indexAction() { $helper = $this->_helper->article; if ($articles = $helper->getArticles(10)) { // fetch 10 articles $this->view->articles = $articles; } // ... |
Using these instances in the View templates is now trivial:
<!-- foo/index.phtml --> <?php foreach ($this->articles as $key => $article) : ?> <div class="<?php echo $article->getType(); ?>"> <span class="headline"> <?php echo $article->getHeadline(); ?> </span> <?php if ($key < 2) : ?> <div class="thumbnail"> <?php echo $this->thumbnailBuilder($article->getImage(0)); ?> </div> <?php endif; ?> </div> <?php endforeach; ?> |
However, there is a "gotcha" with this approach. For a start, there are a lot of things in the Model we don't want the design team to have access to, since they are dangerous functions and mistakes could happen. For example:
<!-- foo/index.phtml --> <?php foreach ($this->articles as $key => $article) : ?> <div class="<?php echo $article->getType(); ?>"> <span class="headline"> <?php echo $article->setHeadline(); // WHOOPS! ?> </span> |
In the event that setHeadline() is a setter function, not only will this small typo not display a headline, if the Model acts as a gateway back to the Service layer, then this view code may have just wiped - permanently - the headline of all ten articles. QA would probably find this; however unit tests would often look at the Controllers - i.e. the business logic back end - rather than testing the View. Selenium tests would pick up that no headlines are showing, but would be unlikely to diagnose that ten articles have had their headlines wiped.
There are a number of other issues with injecting Models, assuming that typos are not your main concern. To aid performance, we want to lazy-load the body, images, and even comments for articles as and when requested. This frees the development team from having to write the exact logic and instead focus on tools to provide designers with. But designers are generally not performance experts - their expertise is interface and creativity - so there is a risk of having inefficient code added using this system.
<!-- foo/index.phtml --> <?php foreach ($this->articles as $key => $article) : ?> <div class="<?php echo $article->getType(); ?>"> <span class="headline"> <?php echo $article->getHeadline(); ?> </span> <?php $image = $this->thumbnailBuilder($article->getImage(0)); ?> <?php // Now loading the thumbnail for every single article.... ?> <?php if ($key < 2) : ?> <?php // ... but only using it twice... ?> <div class="thumbnail"> <?php echo $image; ?> </div> <?php endif; ?> </div> <?php endforeach; ?> |
Then of course, there's always the possibility that we enable the use of code like this from a view ...
<?php $db = $article->getDb(); // Uh-oh... ?> |
In organisations where front end development is done by a third party, this is a serious consideration.
Creating tools that are both useful and yet not overly powerful is a difficult line to walk, and causes either constraints on the Model or on the View, neither being particularly satisfactory. The Model becomes a bloated chimera, full of presentation logic as well as the core business functionality. Designers and front end developers have to wade through a mass of documentation on functionality they care little about just to work out how to get a headline out of an article, or find the author.
The third way...
Any developer that thinks that the View should have almost no logic in them is going to constrict the creativity of their designers, and thereby the site. This is a simple fact of website design - an ambitious front end relies on the tools to deal with a variety of layouts based around factors in the data. "Simple" aspects, such as switching to a different column layout if the number of articles exceeds a given number, need business-grade tools. So, how do we give the designers the tools to be as creative as they like, while letting the developers concentrate on that new cool Services API we would be writing if we weren't busy with presentation logic?
The Model-View-ViewModel pattern is attributed to being the creation of Microsoft as an evolution of the Presentation Model pattern created by Martin Fowler. Aimed at creating presentation logic for XAML-based applications, the logic applies neatly to PHP applications using MVC.
To illustrate the idea, here is an example of how this might work:
<?php class FooController extends Zend_Controller_Action { public function indexAction() { $articles = array(); $helper = $this->_helper->article; $viewModels = array(); if ($articles = $helper->getArticles(10)) { // fetch 10 articles $viewModelHelper = $this->helper->viewModel; foreach ($articles as $article) { $viewModels[] = $viewModelHelper->getViewModel( $article, 'article' ); } $this->view->articles = $viewModels; } } } |
This code sample shows a ViewModel helper, so this would seem like an ideal time to introduce the concept of ViewModels. ViewModels are – in this instance - a combination of the Decorator and Adapter design patterns. They allow the development team to open up a variety of tools to the View without giving the designers access to functionality that was not intended for use in a view setting.
<?php // a very simple ViewModel. class Example_ViewModel_Article implements Example_ViewModel { protected $_model; public function __construct(Example_Content_Article $article) { $this->_model = $article; } public function getName() { return 'Article'; } public function getHeadline() { return $this->_model->getHeadline(); } } |
With this ViewModel, the designers have access to getHeadline() whenever they want, without having access to setHeadline(). We can then continue to map the ViewModel to getBody(), getImage(), and whatever else is needed, without fear of exposing functionality such as setter methods, inappropriately (remember, we are assuming in this example that the Article model has direct access to the Service layer, so that we can lazy load additional information as we need). Here is an extended example to allow our frontend team to load up images, but with restrictions.
<?php // a very simple ViewModel. class Example_ViewModel_Article implements Example_ViewModel { //… protected static $_thumbCount = 0; public function getImage($offset) { if (self:: $_thumbCount > 10) { throw new Exception( 'Erm, design team, are you sure you want to get ten images? Individually? Maybe we should batch fetch these? Call me.' ); } $img = $article->getImage($offset); self::$_thumbCount++; return $img; } |
To avoid having to write an exhaustive list of methods, we could event use the magic methods __get(), __set() and __call() to our advantage:
class Example_ViewModel_ViewModelAbstract implements Example_ViewModel { //… // params to configure ViewModel, not Model protected $_params = array(); public function __get($v) { return $this->_model->$v; } public function __set($var, $val) { $this->_params[$var] = $val; } public function call($method, $args) { if (strpos(strtolower($method), 'set') === 0) { throw new Exception('I will shoot you myself'); } if (strpos($method, 'get') === 0) { return call_user_func_array(array( $this->_model, $method ), $args); } } |
Putting additional presentation logic within the ViewModel now provides us some separation and allows us to add this here, without it feeling like a kludge:
<!-- foo/index.phtml --> <?php // ask the Article ViewModel which template to use echo $this->partial($article->getTemplate(), array('article' => $article)); |
With the ViewModel, we can also make View-specific decisions that may enhance performance. For example, we can set up a ViewModel with a cache object before injecting it into the View, allowing it to cache operations on the Model without resorting to something as brute force as page-level caching. Using the previously mentioned ViewModel helper, we wrap the Model an enclosing ViewModel:
class Example_Controller_Helper_ViewModel extends Zend_Controller_Action_Helper_Abstract // trivial example, assume more to it in production { public function viewModel($model, $viewModelName) { $viewModel = new $viewModelName($model); // Tell it that the View is XML, not HTML $viewModel->setContext('xml'); if ($viewModel->isUbb()) { // check if we need Decorators/parsers $viewModel->addParser(new Ubb_Parser()); } return $viewModel; } } |
If the article has markup, such as UBB, then the ViewModel can also parse the body copy before returning it to the View, or paginate it. All the while, the original Article Model is safely tucked up inside, feeding information to the ViewModel as needed.
To conclude, the ViewModel is an effective design pattern for any website with a requirement to provide detailed encapsulated presentation logic, while still keeping business objects free from needing any View-specific functionality within them. It promotes lazy-loading for performance and necessity, while still maintaining good practice such as dependency injection and segregation of responsibility. And, your design team will love you for it.

24 comments













24 Responses
Stay in touch with the conversation, subscribe to the RSS feed for comments on this post.
Funny, I've implemented this years ago, in almost exactly the same way in ZF, without knowing about the ViewModel pattern....
It may even be doable to implement it more elegantly without having to create the ViewModel objects explicitly (having the view renderer exchange Model objects for ViewModel objects automatically?), but I've never gotten that far.
But I've ditched this approach because it doesn't address the fundamental problem: the people making the Views simply not being competent enough to do so, and this will continue to cause problems along the way, even if you child-proof the model objects. You still hand them a loaded gun, just with a little less ammo.
However, it still looks like an elegant way to map view logic to model objects, even if making the views idiot-proof is not a concern.
I agree to a certain extent, though I think there is a caveat when you state about the people doing the Views "not being competent enough" - having worked alongside a highly competent design team, I can see that their lack of PHP experience does tend to make developers overlook their enormous skills in design, Javascript, and so forth.
In the past when I've implemented this, I also created a Reflection class which allowed them to ask for all the methods and properties of an object, so each object essentially came with its own manual. If I recall, it was
and they would get an output in a div of everything they could access.
No View system can be handed over to a design team without some training, but at least with this system the learning curve can be both less steep, and less dangerous.
This is an interesting approach but I'm not sure if it's really worth the extra maintenance. I mean basically you now have two sets of the same models. Add a field to one, add a field to the other.
As Barney mentions, I'd rather see this done with reflection. Have reflection generate a class dynamically for the purpose, or something like that anyway.
What comes to the whole point of this exercise, it might not actually be that difficult to educate the design team to do things properly in the first place :)
ps. the post comment button looks sort of broken in Opera on OS X.
I would disagree with them being the same models - one is business, one is a design component. Presentation logic is often a very separate beast to business logic.
As to the maintenance overhead of adding additional functions to the business model, the use of __call() and __get() in the ViewModel abstract example third from bottom should make life relatively easy without having to create exhaustive lists in the ViewModel. It should also be relatively simple to create a blacklist of getter methods or properties you specifically don't want the ViewModel to allow access to.
That post perfectly shows why I am not a fan of the ActiveRecord pattern. You simply cannot control where a developer will make changes which directly populate back into the data source. We happily use the DAO layer approach and pass simple PoPo`s (Plain old PHP objects) around. The view developers get the whole object but cannot populate changes back to the data source.
Having an additional ViewModel object which basically wraps and delegates calls seems odd to me. Even in the views you should deal with the same type of domain objects than in the rest of your code.
Unfortunately, I don't think much of the content of this article is correct. I think if you are going to follow the classical MVC approach (which some web devs call MVMC), keep the logic where it belongs. Things like:
if ($articles = $helper->getArticles(10)) { // fetch 10 articles
Which are exampled in almost all the above text, belongs in the view class. All of that is view logic, which doesn't belong in the controller.
So while the concept is a very good one (I've been using mustache view classes for a while now), I think the examples could be a bit better defined.
The example of fetching ten articles is perhaps better placed in the View itself (after all, a designer may want to make it 6 articles, and we're trying to give the View the right amount of tools and power). There is certainly some ambiguity in Zend Framework as to what belongs in a Controller and what belongs in a View. The example above should not be taken as a finalized implementation, but more as a guide.
Solutions like this always seem a little overkill to me. If you really distrust the people creating your HTML templates so much that you have to spend time and energy creating a solution specifically designed to limit their access, you have much larger problems than your code itself. If it's really an issue, let them work in a test environment with replicated (non-live) data. That's what your programmers should already be working with anyways. Programmers are much more dangerous to let around live data and much more prone to making catastrophic mistakes than worrying about the frontend designers.
I can see the concept being good for separating functions of the model that need to produce HTML-friendly data or format things in a certain way specifically for display on the frontend, but even doing that requires an additional maintenance piece for each model, and really just moves the concept from a more generic view helper to a more specific view model. The tradeoffs just aren't worth it. KISS.
The problem you face is caused by the fact you use monolithic models which unite business logic, data access and entity objects. Instead you could separate these abstractions and share only entities with the view.
@Seva: Amen.
@Barney Care to share whatisthis() ?
I definitely like the idea of ViewModels that can only fetch entities and have some level of caching baked-in. Managing full page caching with these kind of mixed pages is a nightmare.
this sounds like something we used donkeys ago in J2EE called the Data Transfer Object (DTO) pattern.
If you are using something like Doctrine 2, then the model objects can be passed to the view as/is. The only way the changes can be persisted is if the view has access to the entity manager...therefore you just pass objects to the view and who care what they do with them. In that situation, seems like a solved problem.
@Steve IIRC it was $this->whatIsThis($obj) and it was a View Helper that simply asked the object its available methods and properties and returned them in a div of class debug so the design team could mouseover any block on the page and get relevant info on it. A later version was planned to use the PHPdoc comments in the class itself as well. This meant the design team didn't need to be constantly trained on new models provided to them, meaning less overhead for the developers in maintenance.
Model-View-ViewModel - ha!
"a Reflection class which allowed them to ask for all the methods and properties of an object, so each object essentially came with its own manual" - @Barney
I like that.
"Solutions like this always seem a little overkill to me ..." - @Vance
How many times must a resultset be looped over before the madness stops?
"The problem you face is caused by the fact you use monolithic models which unite business logic, data access and entity objects. Instead you could separate these abstractions and share only entities with the view." - @Seva
Precisely.
It amazes me how many articles are still written by people that don't understand the difference between a proper Model/View/Controller design, nor ACL.
If the front-end designers can mess up your data, your Controller sucks. Refactor. If your Model has dependencies other than the data, Refactor. If your View has access to anything other than the Controller, get your clients a better programmer!
Why would you treat designers differently than any other user? Try turning this example into a SOAP or RESTful service!
@Clueless What's inside the Article model? Probably a gateway to a SOAP or RESTful service. ACL is not part of this example, though you would hope that it was within the gateways (we do all have time to write that, right?). It's unlikely but it could happen, and the example is in the extreme.
The fact is that there are two separate aspects of an article - business logic, consisting of the data and state, and presentation logic, which covers a vast number of things you don't really want to pollute your business model with. The ViewModel lets you offload all of the essential View logic that is Model-specific without having to create vast numbers of View helpers. Designers will want to be able to use $article->getBody($this->page), not have to go "I need to test for UBB, and then pass it to a Ubb helper, and then paginate based on the article's number of page breaks, and then...". Your View is in danger of being more code then HTML, as every property you want has to be parsed and tested for. Or its in the Controller, which is a clutter. ViewModels keep your code clean, legible, and also give design teams access to the methods they need and removes all the ones they don't. I fail to see how this is a misunderstanding of mVc.
As Rick said, this was implemented ages ago in other frameworks, eZ Publish being the one I am most familiar with. At the time it was implemented there where neither _get and _set magic methods (you might want to look up in fact who where the developers pushing for adding them to the php language ;-) ), nor introspection, so it's implemented in a slightly more complicated way.
The main problems with this approach are, imho:
1. giving the designers so much power they can easily rebuild all business logic in the templates. Great for hacking the system quick and dirty, but it defeats the purpose of the initial separation of concerns
2. lazy-loading of data in the viewmodels is all fine and dandy until you realize designers have no clue at all about what they are loading, and will churn out views that lazy-load the universe and beyond. You will need to build big billboards in the system that warn designers about the total numbers of db queries and memory used by their views, and will eventually resort to giving them access to some output-block-caching construct to improve with performance. But understanding the tradeoffs and subtle interactions of those cache blocks will be way beyond a typical designer's skill.
At that point users of your app will complain that is is dog-slow, when it will be in fact just a tad hard to use proficiently, and you will be able to make a living as a consultant by auditing their developments and recommending they rewrite their views :-)
Continuing the Discussion
http://techportal.ibuildings.com/2010/11/02/creative-mvc-meet-the-viewmodel-pattern/ - The ViewModel pattern.
a must read http://bit.ly/9qDJZ7. ViewModel for complex views. Very clean adapter-type way for view to access model data dynamically