Simple Templating
I’ve seen a lot of code like this in the wild …
$a = <<<EOD <div> my name is $name blah blah blah.... </div> EOD; call_api($a);
If this is the way you typically generate html for a page, I highly suggest you pull your head out of the sand and take a look around. Mahmud ahsan’s post outlines a basic example of output buffering and file inclusion …
ob_start(); include_once "html_markup.php"; $variable = ob_get_contents(); ob_clean(); call_api($variable);
… and I’d like to take it a step further.
This sort of partial rendering is extremely common in any web application. A simple Template class with a static render method (a technique I learned from Sarfaraz Rydhan) is the quickest way to wrap this sort of functionality. Here’s what the class might look like :
class Template { static function render($tpl_name, $d = array()) { $tpl_dir = 'tpl/'; $file_ext = '.tpl.php'; $file = $tpl_dir . $tpl_name . $file_ext; if(file_exists($file)) { ob_start(); include($file); $html = ob_get_contents(); ob_clean(); } else { $html = "Template ". $file ." Not Found." } return $html; } }
Your template might look like this
<?php // [docroot]/tpl/user/greeting.tpl.php $name = $d['name']; $userid = $d['id']; ?> <div class="user">Welcome back, <a href="/users/<?=$userid?>"><?=$name?></a></div>
To use this, you might say
echo Template::render('user/greeting', $user);
or
echo Template::render('user/greeting', array( 'name' => $user_name, 'id' => $user_id, ));
This would render the file tpl/user/greeting.tpl.php with an accessible mixed var $d which might represent a User model instance or an associative array (of key value pairs).
Using this sort of simple templating can encourage more thoughtful template file organization and a more granular template library.
Comments(5)
Kev -
Great post. I’ve just taken a stab at Layouts (a close relative to Templates) and will be using your technique for Templates.
One question I have regarding your example is if in greeting.tpl.php file, your manually declarations of $name = $d['name'], and $userid are a best practice? Or if there is a reason for doing this over using PHP’s extract()?
http://us3.php.net/manual/en/function.extract.php
You could change your Template.class.php to:
And then remove the chunk of PHP at the top of your template and arrive at the same functionality.
Good stuff!
Two more thoughts to bounce off of this post.
One, should there be a check for an object passed in instead of an array? A common case in Recess is going to be passing an Object.
Two, supposing you’re going to render a list of objects/arrays, would it be worth handling this case in the render? Or an alternative render like renderAll? Differences:
vs.
vs.
The upside to keeping the loop within Template’s logic instead of the view’s logic is that the performance will be much better.
Even for rendering lists of objects, I prefer to keep the templates granular.
I find this level of granularity helps encourage the maintenance of a single canonical template for each type of content.
or
I find there’s a balance to be struck between autonomy and completeness. I find that methods like renderAll tend to make me do things like put <li></li> inside a template which really only wants to render a photo thumbnail. Adding <li></li> to photo/thumbnail ties the template to its context, violating its autonomy, keeping me from using it elsewhere. Drawing precise boundaries to keep the code dry requires granular templates. Unless renderAll() took some additional arguments like $before and $after for wrapping output of individual items, but that seems to open a whole can of worms to solve a problem that granular templates solves already.
I’ve been using this technique since a while now and its interesting to see someone else come up with the exact same thing. Then again, this seems to be the solution that most experienced PHP developers will naturally arrive at as a way to effectively use PHP as the templating language that it is.
On another note, a class that only has one static method does not feel like it deserves to be a class since being a class gives it no advantages. Why not just make it a function (template_render)?
http://sandeep.shetty.in/2009/04/procedural-programming-is-not-bad-thing.html
Template::render() does sound rather verby.
I think the benefit might come when you can add additional methods for other templates types, like Template::renderSmarty() where both render and renderSmarty might share some logic that would be best encapsulated by a private method on the Template class.
But you’re right. An autoloaded static class with 1 static method and no properties is essentially a global function.