In this post I’m going to talk about a software architecture pattern we use when we have a number of dynamic content types shared across multiple pages.
It’s called the Hierarchical Model-View-Controller pattern and it is an architectural pattern that would work well for heavily ‘componentized’ sites like BBC News or Guardian.
In a traditional MVC web application, a routing component will handle the URL request. This request will be mapped through to a Controller action, where business logic is initiated and then passed through to the View for rendering.
This works well for simple applications, but once your application grows in complexity, you tend to find you need to share functionality across controllers.
public function indexAction()
{
$promos = $this->get('example.promotion_repository');
$social = $this->get('example.social_repository');
$news = $this->get('example.news_repository');
$locale = $this->get('request')->getLocale();
$viewBuilder = $this->container->get('example.view.builder');
$viewBuilder->assign('latest_news', $news->getByTypeAndPlace('latest', $locale));
$viewBuilder->assign('primary_promotions', $promos->getByTypeAndPlace('primary', $locale));
$viewBuilder->assign('latest_tweets', $social->getByTypeAndPlace('twitter', $locale));
return $this->render('ExampleBundle:Home:Index.html.twig', $viewBuilder->getData());
}
This can be done in a few ways, but typically, it’s by moving the shared functionality into super classes (like an ApplicationController class) or by abstracting this functionality into a service (or similar) which can be initiated in multiple actions.
However neither of these are ideal solutions, as the responsibility for rendering the content ends up duplicated across multiple controller actions.
This is where HMVC comes into play. This pattern allows you to issue sub-requests, which are initiated by the view once it knows what content is required (think of it as a tree of MVC requests).
So if your page is made up of, for example, latest news, tweets, product ranges and advertisement content, you’d have the base controller request and then four additional HMVC requests to provide content for the areas.
{# app/Resources/views/base.html.twig #}{# ... #}
{{ render(controller('AcmeArticleBundle:Article:recentArticles', { 'max': 3 })) }}
So why do we like this pattern? Mainly because it keeps our controller actions small and makes them far more reusable. Each controller action becomes responsible for precisely one thing and this helps us to adhere to SRP.
Try it out, let us know what you think.