Template Extraction

Christopher Pitt
3 min readApr 6, 2014

--

I’m reading this book called Clean Code, by Robert Martin. In it he talks about many aspects of code which make it harder to read and maintain. After a particularly definitive section on function extraction, there’s a small section on closing brace comments…

function getTextCounts($text) {
try {
$characters = 0;
$lines = 0;
$words = 0;

foreach (explode("\n", $text) as $line) {
$characters += strlen($line);
$lines += 1;
$words += count(explode(" ", $line));
} // $text as $line

echo "characters: {$characters}\n";
echo "lines: {$lines}\n";
echo "words: {$words}\n";
} // try
catch (Exception $exception) {
echo "an error occurred.\n";
} // catch
}

Martin argues that these comments, trailing the closing braces of the control structures they are meant to define, are superfluous (in the context of properly extracted functions) and serve only to clutter smaller functions.

This reminded me of the same practise, often promoted, in HTML and template languages. You may find this style of closing-element demarcation familiar…

@section("content")
<table class="posts">
<thead>
<tr class="headings">
<th>created at</th>
<th>title</th>
<th>actions</th>
</tr><!-- .headings -->
</thead>
<tbody>
@foreach($posts as $post)
<tr class="post">
<td>{{ $post->createdAt }}</td>
<td>{{ $post->title }}</td>
<td>
<a href="{{ $post->editLink }}">edit</a>
<a href="{{ $post->deleteLink }}">delete</a>
</td>
</tr><!-- .post -->
@endforeach<!-- $posts as $post -->
</tbody>
</table><!-- .posts -->
<div class="pagination">
{{ $posts->links() }}
</div><!-- .pagination -->
@stop

I have spent many hours deleting these, not because I had any good reason to, but because they were always just mess to me. This got me thinking about how we focus so much on making a score of abstraction layers, in the hopes that our code will be “less messy” or “easier to test” or even just because “Jeffrey said so”, but we often ignore what the templates look like.

Don’t get me wrong; I am all for making great “application” code, and Jeffrey is my hero. I’m just saying we should think about topics like function extraction when we also building templates.

So, the question is: how do we begin to use some of the tricks we’ve learned in the building of templates? Let’s use whatever compartmentalisation the template language affords us to do some template extraction.

I’m going to show this with Blade (the template engine from Laravel), but the concepts can be useful with PHP includes, Mustache partials, or whatever.

Given the template above, how could we extract parts of it out so that we’re left with small parts? How about doing something like this:

@section("content")
<table class="posts">
@yield("content.getHeadings")
@yield("content.getPosts")
</table>
@yield("content.getPagination")
@stop

@section("content.getHeadings")
<thead>
<tr class="headings">
<th>created at</th>
<th>title</th>
<th>actions</th>
</tr>
</thead>
@stop

@section("content.getPosts")
<tbody>
@foreach($posts as $post)
@yield("content.getPost")
@endforeach
</tbody>
@stop

@section("content.getPost")
<tr class="post">
<td>{{ $post->createdAt }}</td>
<td>{{ $post->title }}</td>
<td>
@yield("content.getPostActions")
</td>
</tr>
@stop

@section("content.getPostActions")
<a href="{{ $post->editLink }}">edit</a>
<a href="{{ $post->deleteLink }}">delete</a>
@stop

@section("content.getPagination")
<div class="pagination">
{{ $posts->links() }}
</div>
@stop

If you’re unfamiliar with Blade syntax, you can find the documentation at: http://laravel.com/docs/templates. A brief explanation is that sections store template markup against a key, and yield outputs stored markup by key.

So, the first thing we need to get out of the way is that this approach will lead to more code. We’ve added more whitespace, section and yield tags. This kind of inflation also happens when we extract methods in “normal” code, so that’s nothing to be worried about.

Secondly, there may be a desire (or even a requirement) to move the different “sections” into different files. If you are using a template language which lets you group these sub-templates in the same file (in ways which make sense) then I encourage you to do that. It may become unwieldy if you make six files for every template you attempt to make this kind of change to.

We’ve not changed what this template will result in. Rather we’ve cut it up into smaller, easier-to-read parts. You may not be used to cross-referencing Blade sections (on indeed the template language you are familiar with), but the comments are no longer needed because the scopes are limited and the intent clear.

Will this work for you? I don’t know. Having only just decided to try it, I am going to see how it works for me!

--

--