Monday, September 10, 2007

Application logic versus template logic

By Quentin Zervaas, 1 May 2006


This article takes a look at separating application logic from template logic. We will use the Smarty Template Engine as the basis for the article and its examples.


What this is basically saying is that anything relating to the final output should be determined in the template, while any data handling or processing should be done in your PHP files and classes. There's a little more to it than though, which we'll soon see.


An important aspect of this is that it works both ways. Often people focus on making sure their templates don't do anything application related, but then make the mistake of making their PHP scripts (or classes) doing work that their templates should be doing. We will look at both situations and common issues that arise.


What is application logic?


Application logic (also called business logic) is any code that deals with processing of data. That is, the processing behind the scenes. Common examples of this include:



  • Processing the input from a web form

  • Saving data to a database

  • Debiting money from a credit card

  • Reading or writing cookies on the client's computer


This even includes instantiating the Smarty class and displaying a template:


Highlight: PHP


<?php $smarty = new Smarty () ; $smarty -> display ( ' index.tpl ' ) ; ?>

It also includes generating data that may be used in your template logic, but we'll look at that in a little while.


What is template logic?


Template logic (also called display logic) is any code in your templates that determines how things are displayed. For instance, if you output a certain message if a string is empty, or the string if it isn't empty, then this is template logic. Here's an example:


Highlight: Smarty Template


{if $name|strlen == 0) < p > You didn't enter your name! < p > {else} < p > Hello {$name} </ p > {/if}

Formatting of a string is also template logic (although there may be exceptions, depending on the data). For example:


Highlight: Smarty Template


< p > Title: {$title|strtoupper} Summary: {$summary|truncate} </ p >

We'll look at some more examples of application logic and template logic shortly, for now, we'll look at things that aren't application logic and aren't template logic.


Application logic in templates—bad!


Smarty allows developers to directly embed PHP code by using the {php} {/php} tag. Personally, while it's good to have the functionality available, I think it's almost always a bad idea to ever use this. It is also possible to use any declared PHP function (builtin or user defined) as a modifier (unless you have template security turned on).


An example would be to directly access some data from a form and sanitize it in the template:


Highlight: Smarty Template


< p > This is bad, but you entered {$smarty.get.name|strip_tags|trim}. </ p >

The way this should be done is done in the PHP file, and then passing the sanitized value to the template:


Highlight: PHP


<?php require_once ( ' Smarty.class.php ' ) ; $name = trim ( strip_tags ( $_GET [ ' name ' ])) ; $smarty = new Smarty () ; $smarty -> assign ( ' name ' , $name ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

And then the template would look like:


Highlight: Smarty Template


< p > This is good. You entered {$name}. </ p >

Examples of acceptable logic in templates


So now that I've covered a few bad things, what kinds of things are acceptable? Here are some examples.


Checking a string value or boolean flag (or string length, etc.) to determine what to output:


Highlight: Smarty Template


{if $name|strlen > 8} < p > Your name is long! </ p > {/if} {if $male} < img src = " male.gif " /> {else} < img src = " female.gif " /> {/if}

Alternating background colours


Actually, this is made easy in Smarty with the {cycle} function:


Highlight: Smarty Template


< table > < tr > < th > Field 1 </ th > < th > Field 2 </ th > </ tr > {foreach from=$data item=row} < tr style = " background: {cycle values='#ccc,#ddd'} " > < td > {$row.field1} </ td > < td > {$row.field2} </ td > </ tr > {/foreach} </ table >

Displaying a running count


This is also made easy in Smarty, using the {counter} function.


Highlight: Smarty Template


{foreach from=$items row=item} < p > Item number {counter}. {$item} </ p > {/foreach}

Including another template


This will be required frequently, and as we point out next, the alternative to doing this is quite bad!


Highlight: Smarty Template


{include file='header.tpl'} < p > Example of including a header and a footer. </ p > {include file='footer.tpl'}

Template logic in applications


There are several situations where you may incorrectly end up using template logic in your application without even realising.


Case 1: displaying multiple template files


Take a look at this PHP code to output a single page:


Highlight: PHP


<?php require_once ( ' Smarty.class.php ' ) ; $smarty = new Smarty () ; $smarty -> display ( ' header.tpl ' ) ; $smarty -> display ( ' example.tpl ' ) ; $smarty -> display ( ' footer.tpl ' ) ; ?>

This is extremely bad! You have essentially hardcoded in your application that your templates are built by displaying a header, then the main content, and then the footer. The fact that your templates are split into 3 templates like this is a function of the templates, not of the application.


The correct way to do this is:


Highlight: PHP


<?php require_once ( ' Smarty.class.php ' ) ; $smarty = new Smarty () ; $smarty -> display ( ' example.tpl ' ) ; ?>

And then the example.tpl file would like:


Highlight: Smarty Template


{include file='header.tpl'} < p > Example of including a header and a footer. </ p > {include file='footer.tpl'}

Case 2: Determining a style or an image in code and then assigning it to a template


Let's assume you're outputting a document and you want to show an image that says ‘new' if it was submitted today, or an image that says ‘old' if it was submitted earlier.


It would be bad to specify this image in the code like so:


Highlight: PHP


<?php require_once ( ' Smarty.class.php ' ) ; if ( $article -> isNew ) $image = ' new.gif ' ; else $image = ' old.gif ' ; $smarty = new Smarty () ; $smarty -> assign ( ' image ' , $image ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

Even worse would be:


Highlight: PHP


<?php if ( $article -> isNew ) $image = ' <img src="new.gif" alt="New" /> ' ; else $image = ' <img src="old.gif" alt="Old" /> ' ; ?>

It seems obvious, but I have seen it done. The correct way would be to pass the flag to the template and in the template determine which image to use:


Highlight: PHP


<?php require_once ( ' Smarty.class.php ' ) ; $smarty = new Smarty () ; $smarty -> assign ( ' isNew ' , $article -> isNew ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

And the template:


Highlight: Smarty Template


{if $isNew} < img src = " new.gif " alt = " New " /> {else} < img src = " old.gif " alt = " New " /> {/if}

Afterall, the image is tied to display. You might change template sets or image sets, resulting a new filename. You should only have to change your templates, not your application code.


Exceptions


There are always going to be exceptions where you may need to put application logic in a template, or some circumstances that sit right on the fence. Here's an example:


Highlight: Smarty Template


< input type = " text " name = " title " value = " {$title|escape} " />

In this situation, to deal with the fact a user might enter a quotation mark (”), we must escape the data. You could argue that this is application logic, but personally, I think this is directly related to the output of your HTML document, and so should be escaped when it is output.


Another exception I can think of would be if you wanted to list news articles in your template, but you're not sure how many. That is, the number of articles shown is a function of the template. In one layout you might have room to fit three articles, but if you change templates, you might have room for 5. So how many articles do you assign?


The only way to cover all bases would be to get every single article and assign them all to the template. This seems somewhat excessive. The way I have dealt with this in the past is to use a function call with the number of articles. This function directly calls the database, which as we have said is therefore application logic, but sometimes there's no easier way around things:


Highlight: Smarty Template


{foreach from=$news->getArticles(5) item=article} < h2 > {$article.headline} </ h2 > {/foreach}

Common sense


You must use your own judgement when determining what should be in code and what should be in template. Take a look at this example of checking for an empty array:


Highlight: Smarty Template


{assign var='numElements' value=$myArray|@count} {if $numElements == 0} < p > The array is empty. </ p > {else} < p > There are {$numElements} elements. </ p > {/if}

Strictly speaking, you should determine the count in PHP, and perhaps even a flag to check if it's empty:


Highlight: PHP


<?php require_once ( ' Smarty.class.php ' ) ; $myArray = array ( 1 , 2 , 3 ) ; $numElements = count ( $myArray ) ; $isEmpty = $numElements == 0 ; $smarty = new Smarty () ; $smarty -> assign ( ' myArray ' , $myArray ) ; $smarty -> assign ( ' numElements ' , $numElements ) ; $smarty -> assign ( ' isEmpty ' , $isEmpty ) ; $smarty -> display ( ' example.tpl ' ) ; ?>

And the example.tpl file:


Highlight: Smarty Template


{if $isEmpty} < p > The array is empty. </ p > {else} < p > There are {$numElements} elements. </ p > {/if}

Personally, I think the second method is overkill and I would always use the first method. The additional problem is that we don't necessarily know in the script what data the template requires.


The fact that we do something in the template for an empty array is display specific, not something that needs to be known in the application.


Conclusion


In this article I explained the difference between application logic and template logic when using Smarty. Hopefully you found some simple ways to improve your template design, but also recognised that some complexities can arise when doing this and sometimes comprimises must be made.


One (simplified) way to look at it is like this:


You should be able to completely change templates, including all image files and CSS files, without having to touch any of your PHP files.


All fundamental data required in your templates should be fetched or determined ahead of time and assigned to your templates (this doesn't include meta data, such as the length of a string, etc.).


Further reading


No comments:

About Me

Ordinary People that spend much time in the box
Powered By Blogger