Tribune DataViz

Matters of interest, from the data reporters and developers across Tribune Publishing

Turning WordPress into a framework with MTV

with 30 comments

A couple of months ago, Ryan Nagle described how he and Ryan Mark had unravelled WordPress and reknit it into something prettier for ChicagoNow. Since we finally got around to releasing the code, we thought it might be helpful to share a few thoughts on getting started with the MTV setup.

The beauty is that with MTV, WordPress now works just like any modern web framework. Once you get over the initial weirdness of working with WordPress in this sort of environment, writing themes gets a whole lot more pleasant than it used to be.

Why on Earth is this necessary, you ask? Ryan Mark answers that question in our GitHub docs:

We love WordPress, but we need it to be more serviceable. We need to be able to look under the hood and figure things out quickly. We need a code base that is simple, explicit, legible and self-documenting. That’s what we’re trying to do with MTV.

Development in WordPress becomes a mess very easily. It’s difficult to look at a new theme and figure what’s going on where, because it’s common practice to have a lot of PHP in your HTML and vice versa. Additionally, WordPress packs so much into some functions that sometimes it’s difficult to determine which one you want to track down and modify. If you’re doing heavy customization, you’d better be prepared to spend a good chunk of time playing code detective.

So WordPress functions tend to be extremely implicit. Essentially, MTV creates a more organized, explicit layer on top of WordPress’ native functionality.

Making WordPress behave like a framework removes a lot of hassle by separating markup, request handling, and data manipulation from the site’s core functionality. It’s a little like the way stylesheets separate markup from layout and design.

To make this work, the MTV plugin breaks The Loop and WordPress’ template processing. Fair warning: this means that some of the functions that rely on WordPress “magic” aren’t going to work. If you’re used to writing WordPress themes, this brave new world is going to take a little getting used to. But like the Guide says, “Don’t Panic.”

Let’s walk through how to get MTV up and running. Then we’ll look at how it works by setting up a simple theme.

Before you begin
Check to make sure your host is running PHP 5.3. Because MTV relies on namespaces, a feature introduced in 5.3, earlier versions of PHP won’t suffice.

1. Install WordPress on your local machine (more about that here, though in the latest versions of WordPress, you shouldn’t need to edit the wp-config file manually).

2. Clone MTV from our GitHub account. Drag the “mtv” folder into your wp-content/plugins folder .

3. Install Twig, either manually or with Pear, PHP’s package manager.
With Pear:

sudo pear channel-discover
sudo pear install twig/Twig-1.0.0

Manual install:
Grab the tarball and put the extracted Twig folder in the MTV plugin.

4. Go activate the plugin!

Roll your own theme

A lot has changed since our initial blog post on this subject. This section provides some bare-bones sample code to get you started and referenced the original post where appropriate.

WordPress only requires inclusion of two theme files: style.css and index.php. We won’t be messing with the CSS here, so just make sure you’ve got WordPress’ required comment header in your otherwise-empty stylesheet.  As for index.php:

// Your princess is in another castle.

That’s it. Since WordPress expects this file to exist, we have to have a placeholder file. But we’ll create a template for the index page elsewhere.

Next, we need to register the theme as an app, similar to installing an app in Django. We do this by creating a functions.php:

// Configure MTV
if ( function_exists('mtv\register_app') )
    mtv\register_app('mtv_theme', __DIR__);

// Setup enabled apps
global $apps;
$apps = array('mtv_theme', 'wp');

We’re doing two things here. First, we register the app. This works similarly to registering a function, custom taxonomy or custom post type in WordPress. All we need to provide is the theme’s name and the path it’s on. Since our functions.php is in the same directory as the rest of our theme files, we can just use __DIR__.

Second, we declare a global variable that contains an array of all our installed apps. You can register as many apps as you like. Keep in mind that the order of items in the $apps array matters. When rendering a template, Twig uses this order to look for templates that match. In this case, Tiwg will look in the theme first and then the wp app.

What’s that “wp” app, you ask? That’s part of the MTV plugin. Inside, we register a bunch of WordPress functions we want to use.

Frameworkin’ it

From here, we get into more familiar territory for those of you who have dealt with web frameworks. (If you don’t have framework experience, don’t worry; we’ll explain as we go.) We’ll write some URL regex patterns in urls.php:

 $url_patterns = array(
     '/^$/' =>
    '/(?:[0-9]{4}\/)(?:[0-9]{2}\/){1}(?P[^\/]+)\/?$/' => # year/month/slug
    '/(?P[^\/]+)\/?$/' =>

**Important: Your WordPress permalinks must be enabled in the admin, and they must match the regex for your URLs. We’re using month and name permalinks in this tutorial.

These patterns basically say, “When the URI matches this regex, call this function in views.php.” The patterns are only concerned with what comes after the domain. So in the first object, we’re calling the “home” function when the URI ends after the domain (and possibly a trailing slash). More info on URL patterns is available on our GitHub wiki.

Introducing views

“View” is a bit of a confusing name. It sounds like it should be a page template, but those are, well, templates. (We’ll get to those in a bit.) Views contain the logic that specifies what data is available to your templates. This is where this whole crazy mess comes together. Instead of putting code in our templates, we’ve isolated it in our views.

An example: We said that in our first URL pattern above, whenever a URL ends after the domain, urls.php calls the “home” function in views.php. The home function then does the heavy lifting of assembling the info to serve to the assigned template.

Here’s what a complete view looks like:

namespace mtv_theme_example\views;
use mtv\wp\models\PostCollection,
function home( $request ) {
 	 $args = array('post_type' = 'post',
                  'posts_per_page' => 10,
                  'order' => 'DESC');
     $posts = PostCollection::filter($args);
 	 $template_array = array(
        'posts' => $posts->models

    shortcuts\display_template('index.html', $template_array);

The paths to the functions served in urls.php are based on the namespace we mentioned earlier. The namespace gets declared at the top of views.php:

namespace mtv_theme_example\views; 

Let’s step through writing a view for our index page:

 function home( $request ) { 

When we defined our URL patterns, we set up some groupings to capture data. $request passes the data we captured to our function. The first thing we do is set query flags (also known as conditional tags). WordPress sets these by default, but we reset them in MTV when we break The Loop. So when urls.php calls a view, we reset the query flag in the function. Resetting them makes it possible to use familiar conditionals like is_home, is_page and is_single (including with body_class).

     $args = array('post_type' => 'post',
                  'posts_per_page' => 10,
                  'order' => 'DESC');
    $posts = PostCollection::filter($args);

The heart of this view function is the PostCollection model. Models, very simply, are blueprints of your data. Their contents usually correspond to fields in your database and describe the data’s attributes. The PostCollection model is what grabs the data from The Loop and makes it available for MTV.

Ryan explained PostCollection in his introductory post:

Note that the $args array passed to PostCollection::filter() is exactly the same array you would pass to WP_Query() to get recent posts. The difference is that returned objects are more robust (for example, all post meta/custom fields are included as attributes of returned posts) and have methods consistent with those found in Backbone.js models (for example, get(), fetch(), set(), save()). Also note that these Backbone-like methods do exactly what you think they do. You don’t have to remember the 101 different WordPress functions related to post objects and the variety of possible return values.

MTV’s models are defined in mtv/models.php and mtv/wp/models.php. In views.php, the PostCollection model is included at the top of the file, directly after the namespace declaration:

 namespace mtv_theme_example\views;
 use mtv\wp\models\PostCollection,

Our template loader function is in “shortcuts.” But back to our index view, where we’ll create an array of the data we’re interested in exposing in the template:

    $template_array = array(
         'posts' => $posts->models

    shortcuts\display_template('index.html', $template_array);

That last line loads a template file and passes data to it. Templates only have access to variables that you provide in $template_array. If we ever decide to change our index page, we can just swap out the template reference here and point it somewhere new.


Now that we’ve got a view, we need a template to go with it. We can write clean, readable, easily modified templates using Twig’s syntax. This should be pretty intuitive if you’ve used Django, and Twig has some good documentation if you need to brush up.

Make a templates folder within your theme. Here, we’ve thrown all of our common template code, like doctype, stylesheets, headers and footers, into a base.html template and then extended it to create a lovely index page (or at least a non-blank index page):

{% extends 'base.html' %}

{% block content %}
  <div id="primary">
    <div id="content">{# if we've got posts, show 'em. #}
      {% if posts %}
        {% for post in posts %}
        {# show headlines of recent posts #}
        <article id="post-{{ }}" post="" post_class="">
          <header class="entry-header">
            <h1 class="entry-title"><a title="Permalink to {{ esc_attr(post.post_title) }}" href="{{ post.permalink }}" rel="bookmark">{{ post.post_title }}</a></h1>
       {% endfor %}

      {# if we have no posts, show an "oops, sorry" page. #}
      {% else %}
        <article id="post-0" class="post no-results not-found">
          <header class="entry-header">
            <h1 class="entry-title">Nothing Found</h1>
        <div class="entry-content">'Apologies, but no results were found for the requested archive.</div>
     {% endif %}
   </div><!-- #content -->
  </div><!-- #primary -->
{% endblock %}

As expected, we can use dot notation to access any of the attributes in objects passed to the template via the view.

**Important: You can only use WordPress functions in templates if you’ve made them accessible to Twig. MTV comes with a pared-down list of functions we find the most useful. Ryan’s initial post goes over how this works.

You can’t use WordPress’ functions in Twig templates out of the box.

Thankfully, the overhead of making functions accessible in Twig templates is negligible. This means, however, we were forced to consider what functions we would allow in our templates.

Here’s how to make a WordPress function accessible in Twig templates, using home_url() as an example:

    global $twig;
    $twig->addFunction('home_url', new \Twig_Function_Function('home_url'));

Over time, we compiled a list of all the WordPress functions we needed to get things done. Nothing more, nothing less.

That list of available functions can be found on MTV’s wiki. You can also find an extended version of these sample theme files here.

We hope this will prove a useful way to write clean, easily maintained WordPress themes. Have ideas and want to lend a hand? Find us on GitHub here.


Written by Heather Billings

October 19, 2011 at 8:48 am

Posted in Open Source, PHP

30 Responses

Subscribe to comments with RSS.

  1. this. is. awesome.

    David Johnson

    October 19, 2011 at 11:01 am

  2. That looks like a lot of complicated work to make WordPress work like WordPress. What do you really gain doing this that you can’t do with WordPress?

    You write URL RegEx’s in MTV, just like you do in WordPress

    Your template has an IF statement and a For loop just like you can do with PHP.

    I don’t see what jumping through all of these hoops actually gets you. But thank god WordPress is open source and flexible enough to let you make whatever hoops you want to jump through. Too bad you were a mere two blocks away from where I work this summer. I could have showed you how we do WordPress stuff at the Pew Research Center.

    Russell Heimlich

    October 19, 2011 at 11:44 am

    • You’re right, we’re not making WordPress do anything new here. What we are doing is trying to make it more explicit and organized according to design patterns we like.

      All the HTML is in one place and contains as little code as possible.

      One place for code that interacts with data.

      One place for code that processes requests and responses.

      One place for all the URL regexes.

      And all these places are predictable.

      Ryan Mark

      October 19, 2011 at 12:07 pm

    • As explained at the beginning, in the current WordPress:
      “it’s common practice to have a lot of PHP in your HTML and vice versa”.

      And with regards to MTV:
      “We need a code base that is simple, explicit, legible and self-documenting.”

      Once you’ve used a modem, powerful web Framework, you will never, ever want to go back to the spaghetti code mess of php/html intermix. (I’ve been banging my head on the wall ever since leaving behind a solid web Framework and working with WordPress.)

      I applaud this effort. Great work.


      October 19, 2011 at 12:21 pm

      • Absolutely disagreement. Once I stopped trying to use a “web framework”, and switched to something much simpler and easier to understand, use, and debug, there’s no way in hell I’d ever go back to that ridiculous OOP class-laden hell.


        October 20, 2011 at 11:26 am

    • Basic hello world examples always require a certain degree of imagination. For some quick theme mods, heck, yeah, why not stick to what WP offers out of the box? It’s when your project starts growing that a clean separation of code and templating and other framework-ish features start to make sense.

      Stijn Debrouwere

      October 19, 2011 at 12:58 pm

  3. Ugh. Okay, I have to say, no disrespect intended, but the code examples in this post are causing me physical pain.

    To start with, you’ve implemented yet-another-template-system. Okay, I’m just going to say this outright: Implementing an HTML templating system in PHP is just wrong. PHP *is* an HTML templating system. We don’t actually need another on top of it. It’s just another language to learn which brings no benefits to the table. Learn PHP, and write in PHP. It’s not that hard to do.

    Secondly, Models, Views, and Controllers are great, when you need them for the system. But the MVC pattern does not apply everywhere, and it is especially useless unless you’re implementing a large scale *testing* strategy for each piece. The whole point of an MVC framework is to allow you to write each piece separately, and test it, without having to have all the other pieces in place. So… are you doing any testing of the pieces? If not, you’re wasting your time by separating everything.

    Furthermore, the MVC separation actually makes things harder to debug. When something is wrong on your page, where’s the error? Is it in the data? In the model? The view? There’s no way to tell from just looking because the separation means that you have to trace the problem back through several layers of the onion before finding the root cause. Maybe this separation is good for development, but it’s a pure nightmarish Cthulu-laden hell for maintenance. Especially when you inherit somebody else’s MVC system and have no-freakin’-clue where they thought things would be “obviously” located.

    The URL thing I can take or leave, but frankly I always find it amusing how people want to reinvent the wheel as a square, although in this case it’s more of a hexagonal shape. It rolls, but just a bit slower.

    Basically, the unnamed-but-lets-call-it-procedural pattern which WP uses exists because it works, it’s fast, and it’s easy to use. It’s easy to learn. You don’t need to learn some new and wacky templating language (hey, why not write templates in ASP! :p ). You don’t need to learn somebody else’s MVC patterns. You can generally guess, from glancing at a problem, where the problem lies.

    Okay, you don’t like the way WP does things. I get that. But this whole article reads like you’re saying that I shouldn’t like them either, and I object to that. I prefer the WP way, and the way this MTV system works strikes me as confusing and ugly. It’s overly complicated and my greatest amount of pity goes out to the person who inherits it after you’ve moved on to other pastures.


    October 19, 2011 at 2:10 pm

    • Phew! I love the response this is getting. We obviously hit a nerve.

      We’re not forcing you into anything. This works for us, and from the response I’ve heard so far, it’s going to work for others too. If this is all confusing, I’d humbly recommend you take some time to mess around with it and understand it before trying explaining how much it sucks.

      PHP is not a templating language. It’s a programming language that lets you embed HTML. There are no constraints. And constraints are healthy. Constraints force you to organize.

      ASP is not a templating system. It’s a programming language that lets you embed HTML. Sorta like PHP.

      The nightmare of debugging that you describe is the experience we’ve had with WordPress, and what led us to develop MTV. Themes and plugins mostly invent their own organization and make use of layers upon layers of action and filter callbacks deep down into WP Core. Now that stuff is hard to debug.

      We are working on implementing tests. There are some basic ones in the repo now.

      Tools and design patterns make us better and faster developers. Tools and patterns for developers are mostly missing from WordPress, so we made our own.

      Ryan Mark

      October 19, 2011 at 2:53 pm

      • I am forced to disagree. Constraints are not healthy, because they limit your capabilities and, ultimately, your vision. They force you into somebody else’s pre-defined mold, and make breaking outside that mold difficult, thus making it hard to truly innovate.

        Frankly, if you need constraints to force yourself to organize, then you need to become a better programmer and learn to organize on your own.

        A good computer language should let you shoot yourself in the foot. You just need to learn to aim better.


        October 19, 2011 at 2:57 pm

      • @otto I fundamentally disagree. And I think you’ll find that many of the best people in our industry will also disagree with you.

        Constraints are liberating. Constraints allow me focus on the product, not on the quality and architecture of the code. Constraints are my safety net when I make mistakes. Constraints make it easy for others to understand my code.

        Ryan Mark

        October 19, 2011 at 3:09 pm

      • I suppose that depends on whether you want to be a programmer or a designer. The two are not the same thing, and a programmer shouldn’t need to be artificially constrained by the system.


        October 19, 2011 at 3:11 pm

    • Oh, I forgot to mention – your last point about the maintenance is a serious concern of ours. Which is why we are open sourcing this, writing documentation and making a bunch of noise.

      Ryan Mark

      October 19, 2011 at 2:54 pm

  4. Seems like MTV is un-necessarily complicated, as though you are using only using WordPress to be able to say you are using WordPress. I agree with Otto in this framework is just going to make things harder to debug, and work with ongoing updates in the future.

    WordPress is not for everyone, and that is fine.

    Rachel Baker

    October 20, 2011 at 7:20 am

  5. I think it’s cool. I’d be interested to hear what advantages you find over a light framework like CodeIgniter. Is it that you want to retain the CMS portion of WP as well as extend it to create integrated apps?

    Matt Terenzio

    October 20, 2011 at 11:01 am

    • This stuff grew organically. We considered using Django at first, but didn’t want to run two software stacks. We looked at symfony, but it provided all sorts of stuff that would unnecessarily overlap with WordPress, which is the case with most frameworks. So we wrote a couple small functions that process an array of url regexes and call a view function. It replicates WordPress functionality, but is simpler for us to understand.

      The ORM stuff came later when we wanted an easy to understand way to shuttle data back and forth to Backbone.js on the front-end. Which is why the model stuff is almost identical to Backbone.

      UPDATE: Using WordPress was a given. There is nothing else out there that is as mature and easy-to-use. And we didn’t have to time to build a new CMS in Django. So we build a framework on WordPress.

      Ryan Mark

      October 20, 2011 at 12:44 pm

  6. You guys doing a great job. Ignore those haters.


    October 20, 2011 at 2:14 pm

  7. […] always interested when I hear of projects that attempt to make it even more framework-like. MTV is one of those projects. Some developers at the News Apps Blog Chicago Tribune have recently […]

  8. […] us build the next generation of news websites (and build insane tools to bend WordPress to your will and make it […]

  9. I really like the idea! Being familiar with twig I agree that this improves clarity of a WP app. But… this does break the loop, doesn’t it? And in doing so it breaks the compatibility with countless WP plugins.


    March 9, 2012 at 6:11 pm

  10. Does this framework include an easier way to get custom post types registered with WordPress?
    I’m thinking of something like I guess I would create my own model implementation (or inherit from Post) for my custom post type, but does it get recognized by WordPress?


    May 7, 2012 at 6:54 am

  11. This really seems like a great idea, and I’m definitely going to try it out, unfortunately I see two major issues.

    – Breaking functionality of plugins (the main reason we use WP)
    – Continuing development
    Meaning – what’s to say it won’t break when WP 3.4 comes out and alters one of the 200 global variables it needs to function?

    Either way, all the more to you for making WP a slightly more sane codebase to work with!

    Dan LaManna

    June 12, 2012 at 12:50 pm

  12. […] Homepage | Documentation | Theme tutorial […]

  13. While I really love you’ve tried to do here with the abomination that is php (and by association wordpress), I would have simple moved to using Django with DjangoCMS, Django-Fiber, Django-Mezzanine or Django-FeinCMS.

    the benefits would be :

    1. you’re not trying to sell a bunch of mouth breathing neanderthal wordpress monkies on why MVC and DRY are leaps and bounds better than the current spaghetti situation that is wordpress
    2. you have a MVC and DRY framework as the base
    3. you’re using python (that alone is awesome enough)
    4. your framework has proper development tools
    5. you have native access to Fabric
    6. can I say again that you’ll be using python?
    7. you won’t be using wordpress
    8. you won’t be using php

    Eight fabulous reasons why I think you wasted your time here. Some peasants simply don’t want to improve their lot.


    July 11, 2013 at 9:31 pm

    • While those Django CMS options are great for developing on, they suck for the end-user in comparison with WordPress. A big part of the appeal of WordPress is that the Writing/Editing tools and interface are so well designed. While it might be difficult for us developers to work with, we also have to make sure we’re giving the users the best tools.

      Also WordPress developers are probably going to have a hard time believing you about python and django being better if you call them peasants and refer to their hard work as an abomination. Just sayin’.

      Ryan Mark

      July 12, 2013 at 8:27 am

  14. […] usually start. I’ve talked to many top people in the WordPress community who believe that the current PHP-based templating system is fine. Hard to disagree with them when WordPress powers nearly 20% of the […]

  15. hello!
    i feel frustrated because wordpress-mtv framework not working
    i dive into plugin core and repair something and it work,but maybe not good
    i wonder what happen!!

    Lê Ngọc Ẩn

    March 3, 2014 at 11:08 pm

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s

%d bloggers like this: