Templating a WordPress theme with Twig

Well, that wasn’t as painful as I thought it would be. Some googling and a couple of experiments went a long way, and now I have a partial, unstyled, Twig-based theme happily running on WordPress.

Twig is a templating engine for php. It has more than enough features to get me going, setting it up is as easy as falling off a tree, and I haven’t used it much, which makes it a good candidate for me.

Making WordPress call Twig

Once we’ve downloaded Twig and put it somewhere convenient (in my case, in my theme folder – I’d like to move it out eventually, but it can stay there while I work it out), we need to tell WordPress to initialize the Twig engine. Darko Goles’ blog post, TWIG with WordPress part 1, covers this admirably. In his post, he’s initializing the engine through a plugin, which I wanted to avoid as I didn’t want a theme to depend on a plugin. Luckily the mechanism is the same, since the hooking mechanism in WordPress papers over these issues – we can initialize from a theme exactly like we do from a plugin, by specifying the actions in a functions.php file:

<?php 
    // theme functions.php
    require_once dirname(__FILE__).'/twig.helper.php';
?>
<?php
    // twig.helper.php
    require_once dirname(__FILE__).'/lib/Twig/Autoloader.php';

    class Twig_Helper {
	public static function register() {
	    ini_set('unserialize_callback_func', 
                'spl_autoload_call');
            spl_autoload_register(array(new self, 'autoload'));
	}

	public static function autoload($class) {

            if (0 !== strpos($class, 'Wp_TwigEngine'))
	        return;

            if (file_exists($file = dirname(__FILE__) . '/../' 
                . str_replace(array('_', "\0"), array('/', ''), 
                $class) . '.php')) {
		     echo($file);
		     exit;
                     require $file;
	    }
	}

	...
	...
    }

    ...

    function autoload_twig() {
        Twig_Autoloader::register();
	Twig_Helper::register();
    }

    add_action('init', 'autoload_twig');
?>

The above is copied nearly verbatim from the post I mentioned above, and works fine. It registers twig and loads all the classes it needs to work. I put this in a separate file and referenced it from functions.php so it wouldn’t get mixed in with any theming functions I might need to add later.

Getting to the data

Now we’re able to load templates, but we still need to be able to pass data to them. The solution came, again, from Mr. Goles’ blog, this time from TWIG with WordPress part 2 – we pass a proxy object to the template, which it can then use to call functions on. Again, I used the code from this post almost as provided, except that I used the same TwigHelper class to provide this proxy. I also added another method, text, to wrap the __(string, key) function used for localization.

Writing templates

The template file looks like this:

{% extends "master.html.twig" %}
{% block pageContent %}
	<section id="content" role="main">
		{% for post in posts %}
			{% set format = site.get_post_format() %}
			{% include format 
                               ? "content-" ~ format ~ ".html.twig" 
                               : "content.html.twig" %}
		{% else %} 
			{% include "content-none.html.twig" %}
		{% endfor %>	
	</section>
{% endblock %}

Not much of it, is there? In reality, that’s because most of the layout is defined in master.html.twig, which we’re extending in the first line. This file has placeholders for the pageContent block which is being filled up here, and the rest of the page around it. I’ve also delegated the no-content section to its own file. The snippet above is the equivalent of:

	<div id="content" role="main">
	    <?php if ( have_posts() ) : ?>	
	        <?php while ( have_posts() ) : the_post(); ?>
	            <?php get_template_part('content', 
                                 get_post_format() ); ?>
	        <?php endwhile; ?>
	<?php else : ?>
		<?!-- stuff to show if there's no content -->
	<?php endif; ?>
	</div>

It may not be significantly shorter, but it’s a damn sight easier to read. The readability and size gains are much greater when you have more markup, but I don’t want this to be a templating post.

While… hang on, where’s while?

As I was working on the loop, I realized that Twig doesn’t have a “while” control. This was a problem as WordPress is heavily dependent on The Loop. This can be worked around, as demonstrated in this blog post by Luis Cordova, by making an iterator and using it in a for loop. It’s a good enough solution and the template doesn’t look bad.

Current status

At this point I have a barely functional them (still need to port over a lot of stuff) but getting here took a lot less time than I thought it would. My next step will probably be to port over as much of the old theme as I can, and then work from there.

11 Replies to “Templating a WordPress theme with Twig”

  1. Why would you want to use a template engine (twig) to build a template? This doesn’t make sense to me.

    1. Separation of logic and layout, for one. There’s no reason to have the two combined, and that’s the cornerstone of many different frameworks. I also find it easier to mess around with the layouts when you don’t have any logic mixed in.

      I’m also experimenting with it as I know several junior web developers with very little practical PHP experience who get saddled with stuff like maintaining templates. It’s an educational AND organizational oversight – they should (and will) learn some PHP at least, but it’s no fun being thrown in at the deep end with no test systems, so I’m hoping a templated system will provide a stepping stone for them.

      Then again, I’m from a Java/.Net background, so I’m kind of used to thinking this way because anything else quickly becomes a fantastic mess in those languages. What about you? Do you find the theme system in WordPress fine as it is, or are there any improvements you would suggest?

  2. Yeah I asked because I’m starting to work on some wordpress themes again, and while this idea interests me, I’m thinking “why can’t you just separate the logic into functions that live elsewhere?”. I don’t know enough about twig – or probably theming πŸ™‚ – to know the answer. But I like your reasoning that if someone doesn’t know php at all, then they just use the templating system. But don’t they still need to learn that system? Wouldn’t they be better off learning the little bit of php (or wordpress’ php functions) to do what they need the template to create? Again I need to fully read through your article where you were saying that you run through the wordpress loop entirely and put it in an array and then parse it later and do stuff with twig. It seems interesting. I’m just wondering if it’s overkill.

    I just tried implementing npm, bower, grunt and yeoman to get a quick wordpress install going. It was…. slow. It was so much faster to just download the wordpress install from wp.org and get going that way. Was I missing something? Who knows. Either way, I eventually settled on “I need to get this done, stop f’ing around with tools that are supposed to automate stuff and just start working”.

    It almost seems like “I need to pick the right pen and notebook in order to write better” sort of scenario. Just start writing.

    1. Not sure about the npm/bower/grunt/yeoman, though I’ll have a look at them at some point – found it faster to roll my own build script since it’s not doing all that work.

      Re templating – yes, whoever is using it has to get used to the templating syntax; however, it’s a lot simpler to get used to than the entirety of the wp codex. It’s still kind of messy as presented in this post; in the next one (which you mentioned) it gets a lot cleaner. The idea is that the template writer has a limited subset of functions available, which makes it simpler to manage. If anything needs to be added, it can be added through a single point (index.php), where we’re populating the data for the template. Since they’d only be editing the templates, there’s no risk of them touching the database, messing up the loop, or breaking functions.php (in some cases preventing wp-admin for working. That one was fun.)

      That’s assuming the template writer is a third party. In my case, there’s a subset of features I know I need, from past experience, so not having the rest isn’t really a restriction. And again, if I run into a corner case, I can add it in.

      One thing I see a lot in PHP development is … well, messyness really, no way around that. There’s a low barrier to entry and some “it’s not proper coding” attitude (which I absolutely hate, by the way), so standards tend to fly out the window quite often. Restricting things in this way can keep a few worse practices from happening. It’s not universal, but I’ve seen it often in situations where the PHP product is not the line of business. Disciplined PHP coders exist and I’ve learnt a lot from them.

      “It almost seems like β€œI need to pick the right pen and notebook in order to write better” sort of scenario. Just start writing.” – Yes! I’m a big fan of the hack-test-refactor cycle. In my case I’m refactoring my theme based on observation, a few ideas and so on. In your case, you seem to be looking for an awesome toolset to build your theme on. Just start writing, like you say. See what can be improved. Improve it. Over-engineering and overthinking are massive blockers, and I’m still training myself to avoid them both in code and in life. At least I can say that all the stuff I’m trying out in this refactoring is in response to problems and annoyances I’ve experienced, so maybe, maybe I’m getting a bit better πŸ™‚

  3. Maybe it’s because I’m doing the development and theming that I feel this way. If I were handing off the stuff to someone else at some point I might want to do it differently.

    I’m not looking for an awesome toolkit, I was just trying things out since I’ve heard so much about them. But like you I found it faster to just start writing it.

    I’ll check out the rest of your posts on twig and wordpress. I had come across twig before and was thinking about using it for a normal project, it never even occurred to me to use them for wordpress! Thanks for the posts πŸ™‚

  4. Hi,

    We develop a WordPress plugin called Inject which uses Twig under the hood.
    It allows you to create templates right inside the WordPress admin, and then inject it anywhere in your site with shortcodes. You can create stuff like : posts list, sub-menus, image galeries, well basically anything… Have a look at it here : http://inject.netcod.es.
    If you want more info, or if you want to send us feedback, or maybe what you think about it, feel free to do so.

    Best regards,

    Quentin

    1. Thank you Quentin! Very interesting – I’ll try to have a closer look as soon as possible πŸ™‚

    1. That’s new, thanks! I’ll look into it – don’t think it was around at the time this post was written πŸ™‚

    1. Hello trk! I’m not sure I understand what you’re trying to achieve. If you are trying to pass the results of a mysql query to twig, the normal way is to execute the query from php, process the result, then pass the object to twig. In this sense, it makes no difference to the query if you are going to generate the page in twig or plain old php.

Comments are closed.