Get Smarty

Donate

Paypal

Smarty Icon

You may use the Smarty logo according to the trademark notice.

Smarty Template Engine Smarty Template Engine

For sponsorship, advertising, news or other inquiries, contact us at:

Sites Using Smarty

Advertisement

Best Practices

Although Smarty helps facilitate the separation of business logic (PHP) from presentation, that does not mean it prevents poor implementation. No different than any other software, Smarty has a learning curve. Smarty does not guarantee good application design or proper separation of presentation, this still needs to be addressed by a competent developer and web designer. We'll cover some of the more common pitfalls and how to avoid them.

Note: With Smarty we typically refer to the PHP developer and the template designer as separate roles. Although this could be the same person in real life, it is good to think of them as separate roles so you can clearly understand what belongs in PHP and what belongs in templates.

Now lets get to the first problem: embedding PHP in templates.

1. Do not embed PHP!

This is by far the biggest mistake with Smarty templates. When Smarty was first developed, the {php} tag was provided as a sort of "last resort" way of solving a problem. Experience has revealed that directly embedding PHP is not only unnecessary, it creates more problems than it solves. The {php} tag is strongly discouraged, and is now deprecated in Smarty 3.

Despite the discouragement, lazy developers continue to fall on the {php} crutch, as this may seem (at first glance) to be the quickest path to implementation. However, this approach just leads to more problems. The correct approach is to implement a plugin and do the PHP logic outside of the template.

First lets look at how not to use Smarty.

Example problem: solved the wrong way

Let's say we have this existing PHP function that grabs the current weather forecast and displays it in an HTML table.

PHP

function get_forecast($zipcode) {
    // some weather class we use to do the fetching
    include_once('weather.php');
    // get the forecast from the weather station
    $forecast = Weather::forecast($zipcode);
    // now mark up and return
    $output = "<table>";
    $output .= "<tr><td>Temp:</td><td>".$forecast['temp']."</td></tr>";
    $output .="</table>";
    return $output;
}

So now our PHP developer wants to implement this in a Smarty template. He may choose the seemingly easy route and throw it in with {php} tags:

Smarty

  
  {* this is a comment *}
  {php}echo get_forecast($zipcode);{/php}
  

So we basically "escape" the template for a moment into PHP code and run the function. Uh oh, we have a problem. Our $zipcode variable is a template variable, and it won't "just work" in a {php} block. So we have to get that from the Smarty object. Have no fear, with a little more code we can access that:

Smarty

{php}echo get_forecast($this->getTemplateVars('zipcode'));{/php}

Oops, we have another problem. Our PHP function get_forecast() is not available. We must first include this file. Let's say we have a PHP constant MY_APP defined and we know the filepath relative to that. So we'll throw in an include() statement to include the file:

Smarty

{php}include(dirname(MY_APP).DIRECTORY_SEPARATOR.'forecast.php');
  echo get_forecast($this->getTemplateVars('zipcode'));{/php}

Although this technically works, we just did exactly what Smarty was intended to avoid: We now have a syntactical mess, and we have business logic in our template (PHP constructs and logic that have nothing to do with the presentation.)

Let's say the template designer looks at the output and decides that we don't want the weather in an HTML table. Instead, we want a <div> container with some stylesheet markup. So the designer heads off to the template to change it. Finding this in the template is not a welcome sight. Since there is no way to manage the markup from the template, the designer needs to either inform the PHP developer what to change, or locate and change the PHP code themselves (oops, there goes the separation.) This is probably not the best implementation.

Example problem: solved the right way

Now let's solve the same problem by making a simple template plugin. By implementing a plugin we will keep business logic out of the template, and move all presentational elements into the template, establishing the correct separation. We can either create a new plugin file in the Smarty plugins directory, or we can make a PHP function (or class method) somewhere in our application code and register it with Smarty manually. Let's go with the plugin file method. Here is what our plugin file will look like:

PHP

<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     function.get_forecast.php
 * Type:     function
 * Name:     get_forecast
 * Purpose:  gets the weather forecast
 * -------------------------------------------------------------
 */
function smarty_function_get_forecast($params, $smarty)
{
  // some weather class we use to do the fetching
  include_once('weather.php');
  // get the forecast from the weather station
  $forecast = Weather::forecast($params['zipcode']);
  // now mark up and return
  $output = "<table>";
  $output .= "<tr><td>Temp:</td><td>".$forecast['temp']."</td></tr>";
  $output .="</table>";
  return $output;
}
?>

And now let's implement this in our template:

Smarty

{get_forecast zipcode=$zipcode}

Now this looks a whole lot better. The template now contains something very easy to understand. We are not quite finished: remember that we want to be able to handle the presentation on the template side, so we need to move the markup into the template. This will also simplify our plugin logic, as it no longer has to deal with presentation (as it should be.) Let's make those changes:

PHP

<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     function.get_forecast.php
 * Type:     function
 * Name:     get_forecast
 * Purpose:  gets the weather forecast
 * -------------------------------------------------------------
 */
function smarty_function_get_forecast($params, $smarty)
{
  include_once('weather.php');
  // get the forecast from the weather station
  $forecast = Weather::forecast($params['zipcode']);
  // assign forecast data directly to given template var
  $smarty->assign($params['assign'],$forecast);
}
?>

And in the template:

Smarty

{get_forecast zipcode=$zipcode assign="forecast"}
<div class="forecast">Temp: {$forecast.temp}</div>

We've moved the business logic (PHP) into the plugin, and we moved presentation (HTML/CSS) to the template. We're done! Now compare this solution to the previous example, and you can probably recognize what a programmer and template designer would rather work with. This syntax is very easy to understand and maintain.

Of course, there are other ways to approach this problem. You could simply assign $forecast to the template from the PHP code, foregoing the need to create a {get_forecast} plugin altogether:

PHP

$smarty->assign('forecast',get_forecast($zipcode));

Smarty

<div class="forecast">Temp: {$forecast.temp}</div>

That works too. The first approach allows us to independently fetch the forecast from any template. You can decide for yourself the best way to implement it.

2. Keep PHP constructs separate!

Another common pitfall is embedding PHP constructs (such as objects/classes/functions) into a template when it is better to keep them separate. Let's take an example of editing an article. We display an article on the page and if the current user is an editor, we want to show the edit buttons too. In our PHP logic we have a $roles object we use to check the user's role:

PHP

if($roles->isEditor($_SESSION['user_id'])) { /* do something */ }

The first thing a developer may be tempted to do is assign the $roles object directly to the template and use it as such:

Smarty

{if $roles->isEditor($smarty.session.user_id)}
   ... show edit buttons here ...
{/if}

This introduces several problems. First, we are creating a tight coupling of the underlying PHP object structure to the template, e.g, we can no longer make changes to the application without directly affecting the template. Second, we are introducing application logic into the template (accessing user roles.) Third, this complicates the template syntax, and you expose PHP class/methods/parameters that may not have anything to do with presentation. The template designer does not need to deal with user roles, they only need to know whether or not to show the edit buttons.

Here is an approach to keep things separate. In our PHP logic, we will assign a simple flag to Smarty:

PHP

// assign a flag for Smarty
$smarty->assign('show_edit_buttons',
  $roles->isEditor($_SESSION['user_id']));

And in our template, we use the flag:

Smarty

{if $show_edit_buttons}
   ... show edit buttons here ...
{/if}

Now our template focuses purely on the presentation. In some cases a flag name like $is_editor may be more appropriate, depending on what contexts it is used. But you get the basic idea. Keep the business logic out, and focus on presentation.

Now that said, embedding PHP constructs into templates is a fine line. There may be instances where this works better for your implementation. You have the ability to embed them, just be sure you understand the implications.

3. Keep business logic separate!

It may be tempting to create template functions that do neat things, but remember to keep them focused on presentation and not business logic. Here is a prime example of writing a template function that breaks the separation:

Smarty

{sql statement="select * from categories order by catname limit=$start,$limit" assign="result"}
  {result.catname}
{/sql}

There are several problems here. First, direct SQL statements are business logic. The designer should not need to know anything about SQL or where content comes from, let alone control exactly how this content is retrieved. Second, we open a potential security hole with SQL statements in the templates. The designer could easily break the SQL statement, the parameters could be wrong, or (depending on where your parameters come from) malicious injection attacks could happen. Here is a much better approach to the above example, using a plugin that cleanly separates the business logic from presentation:

Smarty

{get_categories limit=10 assign="result"}
  {$result.catname}
{/get_categories}

In our get_categories block plugin we handle the parameter cleansing and SQL access, and the template focuses purely on presentation. You could also assign $result directly to the template from PHP (probably the more common approach), but this design allows us to retrieve the categories arbitrarily from any template. Use your own judgement for your implementation.

4. How to identify business vs presentation logic

Business logic is normally any logic that does not deal directly with the presentation, or display of content. For instance, regular expressions embedded in the template are normally better handled in PHP, either in a plugin or before content is assigned. Although Smarty ships with a regex_replace modifier, it is normally better to do this in PHP.

Here is an example of using regex_replace. We want to make all emails into links in the description text:

Smarty- business logic in template (bad!)

{$description|regex_replace:"!(\w+@\w+)!":'<a href="mailto:$1">$1/a>'}

It is not immediately obvious what is going on here, unless you are a regex guru. This terse template syntax is a clue that we have business logic in the template. A better approach is to use a custom plugin (modifier):

PHP

<?php
/*
 * Smarty plugin
 * -------------------------------------------------------------
 * File:     modifier.link_emails.php
 * Type:     modifier
 * Name:     link_emails
 * Purpose:  make emails in text into HTML links
 * -------------------------------------------------------------
 */
function smarty_modifier_link_emails($string)
{
    return preg_replace('!(\w+@\w+)!','<a href="mailto:$1">$1</a>',$string);
}
?>

Smarty

{$description|link_emails}

Now it is perfectly clear what is happening, regardless of the template designer's knowledge of regular expressions.

Recall that we keep the template designer and application developer roles separate. The purpose of this is to define a separation between presentation and business logic. If the person editing the templates is quite familiar with PHP, that doesn't mean these rules should not apply. The purpose is to keep the business logic out of the template. This will go a long ways toward fast development, concise template syntax and easy maintenance.

When making decisions where to put logic (in templates or in PHP), try to keep the templates focused on presentation. If you find yourself struggling with template syntax, making syntax too complicated, or trying to access PHP from the templates, there is usually a better approach. Think about the separation, and how to implement it in a way that keeps template syntax minimal and focused on presentation.

Advertisement

Sponsors [info]

Sponsors