In the last post, we looked at getting a Twig generated WordPress theme up and running. I’ll soon begin looking at styling it with SASS and then getting it JavaScript dependencies loaded via Require.js, but first, I’d like to clean up the template code a little. See, last time we were doing something like this to get the posts:
{% 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 %>
Where “posts” was a php iterator that used the WordPress loop. We were also using a proxy class to access wordpress functions in the template. While this works, I wasn’t too happy with the situation in general. The template was making too many decisions and calling functions. Templates shouldn’t be heavy on logic, because their only purpose should be to display stuff, not to decide what gets displayed.
It also fails to address another problem that I find annoying in wordpress – the content tree is generated during the construction of the page, not before it. That means that certain information isn’t normally available while building the header, for example. In the past, I’ve used a workaround which involves multiple queries and resetting the loop, but this time, I wanted it built into the theme.
The solution is as simple as iterating through the loop completely before starting to render the templates. In this way, we can build up everything the template needs beforehand, so it does not need to make any additional calls. Data is accessible all through the template, and the template itself becomes much simpler. Here’s the part of index.php where we’re building up the content:
if (have_posts()) { while (have_posts()) { the_post(); $additional_classes = (is_sticky() && is_home() && !is_paged()) ? "featured" : ""; $p = array( 'id' => get_the_ID(), 'template' => $site->get_format(), 'title' => get_the_title(), 'permalink' => get_permalink(), 'author' => get_the_author(), 'classes' => get_post_class($additional_classes), 'date' => get_the_time(get_option('date_format')) ); if (is_single()) { $p['content'] = get_the_content(); $meta['description'] = get_the_excerpt(); } else { $p['content'] = get_the_excerpt(); if (has_post_thumbnail()) { $image_attributes = wp_get_attachment_image_src(get_post_thumbnail_id(), 'thumbnail'); $p['thumbnail'] = $image_attributes[0]; } } if ($editable) { $p['editlink'] = get_edit_post_link(); } array_push($content, $p); } }
In the excerpt above, you can see that we’re pulling data in from wordpress itself, and adding some metadata of our own. For example,
'template' => $site->get_format()
tells the post which template file it should use, for example, content.html.twig, summary-gallery.html.twig, and so on. The template doesn’t need to make any decisions. We’re also taking the opportunity to make the fields consistent. In
if (is_single()) { $p['content'] = get_the_content(); $meta['description'] = get_the_excerpt(); } else { $p['content'] = get_the_excerpt(); ... }
We’re setting the post content to the value of content if we’re showing a single post, or the value of the summary if we’re showing a list of posts. Both the summary and the full post templates will receive a ‘content’ property that represents their primary content:
<article id="post-{{ p.id ?: "0" }}" class="{{ p.classes|join(" ") ?: "post no-results not-found" }}"> <header class="entry-header row-fluid"> <h1 class="entry-title"> {% block articleHeader %} <a href="{{ p.permalink }}" title="{{ site.text('Permalink to %s')|format(p.title)|e }}" rel="bookmark">{{ p.title }}</a> {% endblock %} </h1> </header> <div class="entry-content row-fluid">{% block articleContent %}{{ p.content|raw }}{% endblock %}</div> <footer class="entry-meta row-fluid"> {% block articleFooter %} <div class="meta">{{ site.text('Posted on <span class="date">%s</span>'|format(p.date))|raw }} {{ site.text('by <span class="author">%s</span>')|format(p.author)|raw }}</div> {% if meta.editable %}<a href="{{ p.editlink }}" class="edit">{{ site.text("Edit") }}</a>{% endif %} {% endblock %} </footer> </article>
content-master.html.twig
<article id="post-{{ p.id }}" class="{{ p.classes|join(" ") }} summary row-fluid"> <a href="{{ p.permalink }}" title="{{ site.text('Permalink to %s')|format(p.title)|e }}" rel="bookmark"> {% if p.thumbnail %} <img src="{{ p.thumbnail }}" class="thumbnail span2" title="{{ p.title()|e }}" /> {% else %} <div class="no thumbnail span2">{{ site.text("no thumbnail") }}</div> {% endif %} <article class="summary span8"> <header class="entry-header row-fluid"> <h1 class="entry-title"> {% block articleHeader %}{{ p.title }}{% endblock %} </h1> </header> <section class="entry-content row-fluid"> {% block articleContent %}{{ p.content|raw }}{% endblock %} </section> <footer class="entry-meta row-fluid"> {% block articleFooter %} {% if meta.editable %} <a href="{{ p.editlink }}" class="edit">{{ site.text("Edit") }}</a> {% endif %} {% endblock %} </footer> </article> </a> </article>
summary-master.html.twig
While the main template now simply looks like this:
{% extends "master.html.twig" %} {% block pageContent %}
{% endblock %}
index.html.twig
The only function I’m still calling directly is site.text, which wraps the wordpress __ function for text localization. This is intentional as I’m planning to replace it at a later stage. Most plugins and systems which are used to support localization depend scan the theme files for uses of that function, and wrapping it in this way can prevent them from working properly. I’ll probably make some sort of localization file for the theme which sets up the localized values, but it’s not something I’ll be looking at right now.
Next up: Styling the template