Thoughts on Templates

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

User avatar
Eran
DevNet Master
Posts: 3549
Joined: Fri Jan 18, 2008 12:36 am
Location: Israel, ME

Re: Thoughts on Templates

Post by Eran »

nicely put, +1
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Re: Thoughts on Templates

Post by Kieran Huggins »

example time!

(this is abstract, not based on an actual implementation. i.e. use your imagination)

A model

Code: Select all

class User extends Model{
  $this->name = null;
  $this->email = null;
 
  function __construct(){
    // this auto-populates the friends list
    $this->has_and_belongs_to_many('user', 'friends');
  }
}
A controller

Code: Select all

class Profile extends Controller{
  // this responds to /profile/username
  function view(){
    $user = User::first($params['username']);
    $this->render();
  }
}
A template

Code: Select all

Hi there <?=$user->name?>!
 
<? if ($user->friends){ ?>
  You have friends!
  <ul>
  <? foreach ($user->friends as $friend){ ?>
    <li><a href="mailto:<?=$friend->email?>"><?=$friend->name?></a></li>
  <?}?>
  </ul>
<? } ?>
Obviously, this view code seems pretty code-heavy, but keep in mind that I've left out 95% of the regular xhtml that would exist in the real page template.

Also, using a template engine like HAML all the closing blocks could be removed in favour of natural indentation:

Code: Select all

Hi there = $user->name.'!'
- if ($user->friends)
  You have friends!
  %ul
    - foreach ($user->friends as $friend)
      %li
        %a{:href=>'mailto:#{$friend->email}'}= $friend->name
So.. thoughts? What logic should be where?
User avatar
allspiritseve
DevNet Resident
Posts: 1174
Joined: Thu Mar 06, 2008 8:23 am
Location: Ann Arbor, MI (USA)

Re: Thoughts on Templates

Post by allspiritseve »

Kieran Huggins wrote:So.. thoughts? What logic should be where?
I think that all looks good (unless of course you're going for true MVC, and want to separate out the view stuff from the controller. Not necessary though.)

I've never really understood why people make such a big deal out of templates. Just use php templates unless there's a security risk.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Thoughts on Templates

Post by Mordred »

Nice, I'm game :)
Code (PHP4, but should be PHP5 compatible):

Code: Select all

<?php
class User {
    var $name, $email;
    var $friends = false;
    function User($name = NULL) {
        $this->name = isset($name) ? $name : str_shuffle("abcde");
        $this->email = "{$this->name}@mailinator.com";
    }
};
 
$me = new User("me");
$me->friends[] = new User();
$me->friends[] = new User();
$me->friends[] = new User();
$loner = new User("loner");
 
require_once('template.inc.php');
 
$tpl = CreateTemplate("tpl.html", ""); //don't mind the second parameter
 
$tpl->AssignObject($me);
echo $tpl->ToString();
 
$tpl->AssignObject($loner);
echo $tpl->ToString();
?>
Template:

Code: Select all

My name is [[name]], write me at [[email]], unless you're a spammer; then, please die.<br>
<hr>
[[]]
    My friends:<br>
    <ul>
    <!--[[friends]]-->
        <li>Friend [[name]] has email [[email]] </li>
    <!--[[/friends]]-->
    </ul>
[[]]
    Brain the size of a planet, and yet I don't have any friends.
[[/]]
Output:

Code: Select all

My name is me, write me at me@mailinator.com, unless you're a spammer; then, please die.
My friends:
Friend dbace has email dbace@mailinator.com 
Friend cbeda has email cbeda@mailinator.com 
Friend daebc has email daebc@mailinator.com 
My name is loner, write me at loner@mailinator.com, unless you're a spammer; then, please die.
Brain the size of a planet, and yet I don't have any friends.
Explanation:

The idea is to identify common "boring" tasks and hide them in a higher-level syntax in the template. Then PHP code is generated to deal with the semantics.

The meaning of [[var]] is straightforward replacement. There are some details which I won't go into now.
$tpl->AssignObject($me) automatically fills all $me->var to [[var]] ... notice how following the same naming reduces the entire setup to one single line of code. There are other benefits of this style not visible in this example as well; they are related to other primitives of my framework which are OT to describe here.

Back to the template:

Syntax twist 1: the conditional (think "switch" rather than "if")

Code: Select all

[[]]
  block 1 (with [[vars]])
[[]]
  block 2 (with [[other_vars]])
[[]]
  block 3 (with no vars)
[[/]]
Depending on which variables are "set" on the templates ("unset" means !isset(), a boolean "false" or an empty string .. should maybe extend it to empty array as well), the first appropriate block is chozen. This is how we deal with the case where $this->friends is false - the second block is chosen instead of the first one. There are other tricks that follow from the way conditionals work, for example setting boolean variables to true. They themselves will not be rendered, but will serve as "triggers" to choose the right block in the conditional.

Notice that the syntax prevents the conditional blocks from supporting nesting. It was deliberately designed that way - if it's something so complex, do it in PHP!

Syntax twist 2: the "loop"

Code: Select all

<!--[[var]]-->
  block (with some variable [[element]])
<!--[[/var]]-->
 
It works when [[var]] is an object or an array, or - as is the case - an array of objects (or arrays). The vars inside the block are either $var['element'] or $var->element (respectively $var[$key]['element'] and $var[$key]->element in the multidimentional case). One-dimensional numeric-index arrays are supported with [[_]] being the "current" element.

That's all :)
Because of the high expressiveness on the template syntax and the PHP API, both the PHP code and the template remain short. The template also remains more or less valid HTML (if you wrote it that way, of course ;) ).
Last edited by Mordred on Tue Jun 10, 2008 10:55 am, edited 1 time in total.
hansford
Forum Commoner
Posts: 91
Joined: Mon May 26, 2008 12:38 am

Re: Thoughts on Templates

Post by hansford »

why don't we all go take a design course, fire all of the designers, and then we can write it any way we see fit.
User avatar
superdezign
DevNet Master
Posts: 4135
Joined: Sat Jan 20, 2007 11:06 pm

Re: Thoughts on Templates

Post by superdezign »

hansford wrote:why don't we all go take a design course, fire all of the designers, and then we can write it any way we see fit.
Or, an alternative would be to help designers learn OUR ways instead of vice versa in order to keep the efficiency that comes with separation of duties.
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Thoughts on Templates

Post by Christopher »

Mordred wrote:Nice, I'm game :)
Code (PHP4, but should be PHP5 compatible):
PHP4? :) I'd be interested in PHP5...

A simple library I use has one level of blocks and only has replacement tags. I'd think loops are a good addition. It's templates look something like this:

Code: Select all

<!--[[page]]-->
My name is [[name]], write me at [[email]], unless you're a spammer; then, please die.<br>
<hr>
    My friends:<br>
    <ul>
    [[friends]]
    </ul>
    Brain the size of a planet, and yet I don't have any friends.
<!--[[friends]]-->
        <li>Friend [[name]] has email [[email]] </li>
 
You code would have to insert the friends block as the friends tags.

I don't exactly understand what "[[]]" does...
(#10850)
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Thoughts on Templates

Post by Mordred »

arborint wrote:PHP4? :)
Don't ask... and soon I'll have to make a transition with all compatibility fun that ensues.
arborint wrote: A simple library I use has one level of blocks and only has replacement tags. I'd think loops are a good addition. It's templates look something like this:

Code: Select all

<!--[[page]]-->
My name is [[name]], write me at [[email]], unless you're a spammer; then, please die.<br>
<hr>
    My friends:<br>
    <ul>
    [[friends]]
    </ul>
    Brain the size of a planet, and yet I don't have any friends.
<!--[[friends]]-->
        <li>Friend [[name]] has email [[email]] </li>
 
You code would have to insert the friends block as the friends tags.
I don't get how you know where the blocks start and end - maybe the start of one is the ending of the previous one? Ah, I get it, you have consequently defined blocks, and if any of them is mentioned by name, it is substitited? In my system it is done only in-place, which has some downsides (but was easy to implement, that's why I haven't decided whether to change it or not).
arborint wrote:I don't exactly understand what "[[]]" does...
It's used to define a conditional statement. In your version of the template, there is no check whether we have "friends" or not. In my system it's used as a delimiter, defining alternative blocks (the [[/]] marks the end of the last alternative block). So this:

Code: Select all

[[]]
  block 1 (with [[vars]])
[[]]
  block 2 (with [[other_vars]])
[[]]
  block 3 (with no vars)
[[/]]
defines three alternative blocks. They are "tested" consequentially, and the first to "succeed" is chosen. In simplified pseudocode it will look something like:

Code: Select all

if (isset($vars)) echo "block 1 (with $vars)";
else if (isset($other_vars)) echo "block 2 (with $other_vars)";
else echo "block 3 (with no vars)";
The actual tests are more elaborate (already explained in the previous post), and allow for a short way to describe the conditional semantics. I am quite pleased with the expressiveness I got (after several steps of evolution of course) from this idea, without sacrificing the ease of use and execution speed. I don't know how it looks to another pair of eyes though, so please, do comment everything you dislike (or like!).
User avatar
Christopher
Site Administrator
Posts: 13596
Joined: Wed Aug 25, 2004 7:54 pm
Location: New York, NY, US

Re: Thoughts on Templates

Post by Christopher »

I guess [[]] does not seem very expressive to me because I had to ask! ;) What if you want multiple tags in a block? How does it determine which of the blocks to select?
(#10850)
mpetrovich
Forum Commoner
Posts: 55
Joined: Fri Oct 19, 2007 2:02 am
Location: Vancouver, WA, USA

Re: Thoughts on Templates

Post by mpetrovich »

OK, here's a static "replace" template, with "theme" elements.

-------The Main Template -------

Code: Select all

$MainTemplate = 'My name is @@NAME@@, write me at @@EMAIL@@, unless you\'re a spammer; then, please die.<br>
<hr>
@@FRIENDLIST@@
@@NOFRIENDS@@';

------- Elements in Theme ---------

Code: Select all

$FriendRowTpl  = '<li>Friend @@NAME@ has email @@EMAIL@@</li>'; 
 
$FriendListTpl = 'My friends:<br />
    <ul>
      @@FRIENDROWS@@
    </ul>';
 
$NoFriendsTpl  = 'Brain the size of a planet, and yet I don\'t have any friends.';


----------- Controller ----------

Code: Select all

$friends = '';
if ($user->friends) {
    $NoFriendsTpl = ''; // clear out NoFriends
    $FriendRows = '';
    foreach ($user->friends as $friend){
        $FriendRows .= str_replace(array('@@NAME@@', '@@EMAIL@@'), array($friend->name, $friend->email), $FriendRowTpl)."\n";
    }
    $FriendListTpl = str_replace('@@FRIENDROWS@@', $FriendRows , $FriendListTpl);
} else {
    $FriendListTpl = '';
}
 
echo str_replace(
   array('@@NAME@@', '@@EMAIL@@', '@@FRIENDLIST@@', '@@NOFRIENDS@@'), 
   array($use->name, $user->email, $FriendListTpl, $NoFriendsTpl), 
   $MainTemplate);
 
I don't know if this is any better, although it does not require a template engine. It separates the HTML into multiple variables, that can be good or bad. It makes if flexible if you are working on a theme, and you could change language or style if so desired.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Thoughts on Templates

Post by Mordred »

arborint wrote:I guess [[]] does not seem very expressive to me because I had to ask! ;) What if you want multiple tags in a block? How does it determine which of the blocks to select?
Agreed, maybe a more clear markup is required for this case, I'll think about it. Maybe I should even support named blocks, so they can be nested.

By "expressive" I meant "says/does a lot in a few chars", not (apparently) "says it clearly so anyone will understand it without prior explanation".

There is support for multiple variables to be substituted (tags? why tags?), the generated code will check if all of them were "set":

Code: Select all

[[]]
  we have [[var1]] and [[var2]]
[[]]
  we have [[var1]] only
[[]]
  we have [[var2]] only
[[]]
  no vars defined
[[/]]
In pseudocode:

Code: Select all

if (isset($var1) && isset($var2)) echo "we have $var1 and $var2";
//etc...

@mpetrovich: This is a template engine, of sorts :) It's one of the most popular variants of homebrew template engines, so thanks for showing your own. Some problems are:
1. One must "manually" loop the loops and enumerate the vars to be replaced
2. Runtime string replacement is ... slow.
3. The template snippets are separated (assuming you'll read them from file or something), which, if one wants to change something, make it hard to identify in which piece a particular string resides.

Regarding point 3, since it's a universal problem for all template systems, I have a "debug" mode which colors all snippets with different colors and adds javascript onclick events to tell you exactly which templates were loaded to generate a particular piece of HTML.
User avatar
RobertGonzalez
Site Administrator
Posts: 14293
Joined: Tue Sep 09, 2003 6:04 pm
Location: Fremont, CA, USA

Re: Thoughts on Templates

Post by RobertGonzalez »

Why would you want to add the extra processing logic of assigning/replacing variables/values? I thought, for a while, that a template engine was THE thing to do to separate business logic and presentation logic. I was totally wrong.

Presentation logic is the process of rendering, to the client, data in a fashion suitable for receiving by the client. Views do that. The controllers receive requests and provide responses (usually through request and response objects) while communicating with models to manage the flow and interaction of data.

The controller does not care how things need to be shown, only that they are shown. The view needs to care. Since the data is available to the views through the controllers, why add the step of taking the data that is held by the controller and substituting that for variables in the view that essentially written in another language (whether it is smarty, @@ syntax, [[]] syntax or even {} syntax, ultimately it becomes a totally new language to manage - if a designer doesn't know PHP and has to learn this new template language... well crap, just have them learn PHP)?

I have been using a lot of stripped down PHP code in my views for the last year or so. I have moved away from variable substitution mechanisms and template engines because to me it just seems silly to replace PHP with something written in PHP when PHP does exactly what you are doing with the replacement, only without the extra processing.

Code: Select all

<?php
/**
 * App_Controller here could extend Framework_Controller_Page or something similar
 */
class App_Controller_About extends App_Controller { 
  public function __construct() {}
 
  public function actionSitemap() {
    $this->assign('sitemap', $this->model->getSitemap();
  }
}
?>

Code: Select all

<?php
class App_Model_About extends App_Model {
  public function getSitemap() {
    return $this->mysql->call('get_sitemap');
  }
}
?>

Code: Select all

<div id="sitemap">
  <?php if ($sitemap->hasRecords()): ?>
  <ul>
    <?php for ($sitemap->startRecord(); $sitemap->hasRecord(); $sitemap->nextRecord()): ?>
      <li><?php echo $this->anchor($sitemap->fields['url'], $sitemap->fields['name']) ?></li>
    <?php endfor; ?>
  </ul>
  <?php else: ?>
  <p>There is no sitemap.</p>
  <?php endif; ?>
</div>
Yes, there is code in the template. Yes, it is a little ugly. But seriously, there is going to have to be code somewhere in your app. What is wrong with having it right here, in small enough doses to get the job done and in a fairly unobtrusive manner?
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Thoughts on Templates

Post by Mordred »

Everah wrote: thought, for a while, that a template engine was THE thing to do to separate business logic and presentation logic. I was totally wrong.
I'm very interested in what turned you away.
Everah wrote: Since the data is available to the views through the controllers, why add the step of taking the data that is held by the controller and substituting that for variables in the view that essentially written in another language (whether it is smarty, @@ syntax, [[]] syntax or even {} syntax, ultimately it becomes a totally new language to manage - if a designer doesn't know PHP and has to learn this new template language... well crap, just have them learn PHP)?
I don't buy the idea that a designer will write the templates, I'm not sure how often that happens in the real world, but I don't think non-programmers can handle the full Smarty syntax.
I agree that templates are another language to learn. What I don't agree with is that they are comparable to PHP. Even Smarty's heavy syntax is easier to grasp, than - for example - the function calling, iterators, access to object members and arrays we see in your "view" snippet.

While PHP templates are possible, the code is really "ugly", in the sense that either it is short, but uses global variables, or it is proper OO and longer and harder to read. Separating code from HTML is not a purely "aesthetic" need though, besides "ugly" the code is harder to maintain - imagine making changes to a set of three different skin templates. Or having a view that displays a model object and one that shows a form to edit the same object, with the same layout, but with edit controls instead. With substituting templates you can handle all these quite easily. If you implement the example Kieran posted with OO PHP templates, the size and complexity difference will be obvious.

So, if it won't be PHP, but another language, should we make it PHP-esque, like Smarty, trivial, i.e. a substitution-only template engine with all logic offset in the PHP, or a language that, in the limited domain of substituting data in pieces of text, has more expressive power than PHP. In the end, semantically all three are the same - they get the job done. As matthijs eloquently put it:
matthijs wrote:Those variables need to be inserted. Those loops (show latest 10 articles) need to be performed. That "display logic" has to be run. One way or the other.
What I'm looking for here is that other way, the shorter way. The way that's faster to write and easier to read. The way that doesn't get it your way. The way I experiment with (by the way) is incidentally very designer-friendly, although I don't see it as a particularly important feature, rather a nice side effect (but let's not forget that whoever ends up writing that template, it's good to make it easy for him ... hey, it could be me!). My, that's way too many "way" in this paragraph :)

Does the job. Short. Easy to read. Easy to maintain. Steals from the rich and gives to the poor.
I think that so far the template system (both the "language" and the PHP counterpart) I came up with has these features, and I believe that these features are important. Umm, not sure of the last one. But anyway, feel free to debunk these two claims, I do need other angles on my thoughts.
matthijs
DevNet Master
Posts: 3360
Joined: Thu Oct 06, 2005 3:57 pm

Re: Thoughts on Templates

Post by matthijs »

If you need
Mordred wrote:Does the job. Short. Easy to read. Easy to maintain. Steals from the rich and gives to the poor.
you can indeed look at some of the available template languages. But you can also use PHP, but then just use a limited amount or a specific set of variables/logic.

Take a look at some of the blogging tools/cmses, for example wordpress. It's pure PHP in the templates, but it's not too difficult. Even noobs who build their first site can get it done with the help of the manual.

Code: Select all

 
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
 
<div class="post" id="post-<?php the_ID(); ?>">
  <h2 class="posttitle"><a href="<?php echo get_permalink() ?>" ><?php the_title(); ?></a></h2>
    <div class="entry">
      <?php the_content('<p>Read the rest of this entry</p>'); ?>
    </div>
</div>
 
The argument that the template builders (designers?) are not able to deal with those few lines of PHP in the templates is an argument I don't accept. If you're smart enough to write accessible HTML, deal with all those IE bugs and get your site working cross-browser, you are smart enough to understand those few snippets of code between <? and ?> tags :)
User avatar
Kieran Huggins
DevNet Master
Posts: 3635
Joined: Wed Dec 06, 2006 4:14 pm
Location: Toronto, Canada
Contact:

Re: Thoughts on Templates

Post by Kieran Huggins »

In practice, I've found that there are 3 breeds of "designers":

Photoshoppers will design the site as a series of Photoshop mockups, then pass it off to "webify". This is the most fundamental of the designer types, but still useful since proper design is a fairly complex and often misunderstood skill set.

XHTML/CSS Ninjas will take it a step further by turning their designs into functional xhtml and css. I prefer working with these folk since it means less work for me, and it also implies a certain fundamental understanding of the interactivity component of design. I don't mind templating at all, especially since it gives me a chance to review the output.

Designers in programmers clothing can basically do it all, minus the "back-end" wizardry (we're wizards, don't you know). These are a dangerous breed, IMO, as they know enough code to be dangerous and/or opinionated (read: stubborn). While they can typically handle the logic in the templates, they tend to be more difficult to work with in my experience.

I'm happy with #1 & #2, but usually get better results from #1.

Also, there's still the issue of javascript / behaviours that we haven't touched on yet, but jQuery makes that a snap ;-)
Post Reply