PHP DevCenter

oreilly.comSafari Books Online.Conferences.

We've expanded our LAMP news coverage and improved our search! Search for all things LAMP across O'Reilly!

Search
Search Tips

advertisement

Listen Print Discuss Subscribe to PHP Subscribe to Newsletters

The Dynamic Duo of PEAR::DB and Smarty

by Joao Prado Maia
04/17/2003

Our Goals

Total separation of business and template logic is an often-sought goal in advanced web application development. Many times, PHP leads to a compromise. Some code sets implement features by embedding markup in the business logic. Others put business logic in the template side.

While this was true even for my own software development, recently I came to the conclusion that it didn't need to be that way. By simple trial and error, developing new versions of my own libraries and reusing other libraries for my projects, I have found a better way to integrate a database-backed application with a powerful template engine.

I'm obviously talking about PEAR::DB and Smarty here, and I will show throughout this article my experiences and best practices in integrating these two amazing libraries. You will end up with a really nice setup, having an excellent separation between business logic and template logic.

Database abstraction packages are a common part to most programming languages, such as Java's JDBC or Perl's DBI. PHP is no different -- there are countless database abstraction libraries available. PEAR::DB is one of the newer data access abstraction packages, and it was described in my article PEAR::DB Primer. Template libraries are also a pretty common feature of most web related technologies, such as Perl or Cold Fusion. Smarty is a template engine maintained by Monte Ohrt. Andrei Zmievski, a core PHP developer, also contributed to its development. Smarty is different from the existing solutions because of its simplicity and available features, such as the ability to cache templates into PHP scripts which gives a big boost in performance.

The Setup

While I do love to work with PEAR::DB and Smarty, I always try to make my code as "future-proof" as possible. I want to develop my PHP scripts in a way that is forward-compatible (as much as possible) with whatever template engine I wish to use.

That basically means creating a generic wrapper class that hides away the specific features of Smarty. This is the class that I usually use in my personal projects:

<?php
require_once(APP_SMARTY_PATH . "Smarty.class.php");

class Template_API
{
    var $smarty;
    var $tpl_name = "";

    function Template_API()
    {
        $this->smarty = new Smarty;
        $this->smarty->template_dir = APP_PATH . "templates/";
        $this->smarty->compile_dir = APP_PATH . "templates_c";
        $this->smarty->config_dir = '';
    }

    function setTemplate($tpl_name)
    {
        $this->tpl_name = $tpl_name;
    }

    function assign($var_name, $value = "")
    {
        if (!is_array($var_name)) {
            $this->smarty->assign($var_name, $value);
        } else {
            $this->smarty->assign($var_name);
        }
    }

    function bulkAssign($array)
    {
        while (list($key, $value) = each($array)) {
            $this->smarty->assign($key, $value);
        }
    }

    function displayTemplate()
    {
        $this->smarty->display($this->tpl_name);
    }

    function getTemplateContents()
    {
        return $this->smarty->fetch($this->tpl_name);
    }
}
?>

APP_PATH and APP_SMARTY_PATH are constants defined in a configuration file. They specify the path on the server where my scripts are stored and where the Smarty library is located, respectively. I usually like to bundle Smarty and PEAR::DB with my own applications, to prevent against problems arising if the system administrator decides to upgrade the system-wide PEAR library.

In any case, the wrapper class above allows me to have scripts that would look similar to the one below:

<?php
include_once("config.inc.php");
include_once(APP_INC_PATH . "class.template.php");

$tpl = new Template_API();
$tpl->setTemplate("example.tpl.html");

$tpl->assign("links", "list of links goes here");

$tpl->displayTemplate();
?>

APP_INC_PATH is, as you probably guessed, the path on the server where my classes are stored--my "include" directory.

You also probably noticed that the script above is totally clear of any markup or related layout logic. This is still a very simple script, but you will continue to see the same type of code when using this kind of system.

Smarty Template Engine Functions

Some of the most useful features of Smarty are in the wide variety of functions available to use in the template itself. It might seem odd to be able to use functions from inside of the templates, since the whole idea is to keep the logic separate from your layout. This is template logic however, not business logic.

I often use the html_options function. Below you will find a few simple examples of its use:

<?php
include_once("config.inc.php");
include_once(APP_INC_PATH . "class.template.php");

$tpl = new Template_API();
$tpl->setTemplate("example.tpl.html");

// first select box here
$states = array(
    "CA" => "California",
    "TX" => "Texas",
    "NY" => "New York"
);
$tpl->assign("states", $states);

// second select box here
$abbreviations = array("CA", "TX", "NY");
$titles = array("California", "Texas", "New York");
$tpl->assign(array(
    "abbreviations" => $abbreviations,
    "titles"        => $titles
));

$tpl->displayTemplate();
?>

This example builds the options straight from the associative array:

<select name="states">
  {html_options options=$states}
</select>

This example builds the options from two separate arrays of values:

<select name="other_states">
  {html_options values=$abbreviations output=$titles}
</select>

This idea is easily integrated with features available from PEAR::DB. The use of the get*() functions is very handy, and appropriate in these cases. Notice the example below:

<?php
include_once("config.inc.php");
include_once(APP_INC_PATH . "class.template.php");

$tpl = new Template_API();
$tpl->setTemplate("example.tpl.html");

include_once("DB.php");
$dbh = DB::connect("mysql://user:passwd@server/database");

$stmt = "SELECT abbrev, title FROM states";
$states = $dbhl->getAssoc($stmt);
$tpl->assign("states", $states);

$tpl->displayTemplate();
?>

That's it--instant integration between your database abstraction package and your template engine library.

Handling More Complex Result Sets

While html_options is a very convenient way to build select boxes straight from a database result set, Smarty has several other interesting features that will be valuable when using a more complex setup. It's often useful to fetch a complex database result set and pass it to the template. The following example does exactly that:

<?php
include_once("config.inc.php");
include_once(APP_INC_PATH . "class.template.php");

$tpl = new Template_API();
$tpl->setTemplate("example.tpl.html");

include_once("DB.php");
$dbh = DB::connect("mysql://user:passwd@server/database");

$stmt = "SELECT * FROM news ORDER BY news_id DESC";
$news = $dbhl->getAll($stmt, DB_FETCHMODE_ASSOC);
$tpl->assign("news", $news);

$tpl->displayTemplate();
?>

Here is the companion template:

<table>
  <tr>
    <td>ID</td>
    <td>Title</td>
    <td>Author</td>
  </tr>
  {section name="i" loop=$news}
  <tr>
    <td>{$news[i].news_id}</td>
    <td>{$news[i].news_date}</td>
    <td>{$news[i].news_title}</td>
    <td>{$news[i].news_author}</td>
  </tr>
  {/section}
</table>

The section function works like the for loop construct in PHP. A common enhancement is to show things in your templates only when a specific condition happens, like so:

<table>
  <tr>
    <td>ID</td>
    <td>Title</td>
    <td>Author</td>
  </tr>
  {section name="i" loop=$news}
  {cycle values="#CCCCCC,#999999" assign="row_color"}
  <tr bgcolor="{$row_color}">
    <td>{$news[i].news_id}</td>
    <td>{$news[i].news_date}</td>
    <td>{$news[i].news_title}</td>
    <td>{$news[i].news_author}</td>
  </tr>
  {if $smarty.section.i.last}
  <tr>
    <td colspan="4">
      Total of {$smarty.section.i.total} news entries found.
    </td>
  </tr>
  {/if}
  {sectionelse}
  <tr>
    <td colspan="4">
      No news could be found.
    </td>
  </tr>
  {/section}
</table>

This example introduced several new features:

  • The cycle function takes a comma-separated list of values and assigns each to the row_color variable. This produces a different row color for each loop iteration.
  • The $smarty reserved variable holds loop information. You can test if the current iteration is the last one by examining $smarty.section.i.last, and can get the total number of rows in the $news array of data with $smarty.section.i.total.
  • The sectionelse part shows different information if the $news data array is empty. You can use this to show a "There were no results found"-type message in your templates.

Separating Template Files

Like any PHP script, many applications grow so complex that it's best to separate small features into their own scripts. The same thing happens in templates. Often, it's easier to include other snippets than to duplicate the same HTML in several files.

A typical Smarty-based application might resemble:

{include file="header.tpl.html"}
{include file="ads.tpl.html"}

<!-- content goes here -l->

{include file="copyright.tpl.html"}
{include file="footer.tpl.html"}

This is very useful to make your application more modular and flexible to changes. You usually want to show the copyright notice in all pages of your web site, but you don't want it in pop-up windows or other simple pages. Moving the copyright notice to its own template can make your applications more flexible in regards to template organization.

The include template function also supports some advanced features. You can pass flags to your templates to control various output options, for example:

{include file="header.tpl.html" title="special title"}

Let's say you usually have:

<title>website name - slogan goes here</title>

In your header.tpl.html template, you could have the following:

<html>
<head>
{if $title != ""}
	<title>website name - {$title}</title>
{else}
	<title>website name - slogan goes here</title>
{/if}
</head>
<body>

That's a pretty simple example, but it shows you what kind of logic you can put in your templates.

Conclusion

I hope this article demonstrated some of the best features of integrating PEAR::DB and Smarty. Have fun and most of all, remember to read the manuals!

  • PEAR Manual
  • Smarty Manual

Joao Prado Maia is a web developer living in Houston with more than four years of experience developing web-based applications and loves learning new technologies and programming languages.


Return to the PHP DevCenter.


Do you have any clever tricks for using Smarty and/or PEAR::DB? Share them here.
You must be logged in to the O'Reilly Network to post a talkback.
Post Comment
Main Topics Oldest First

Showing messages 1 through 14 of 14.

  • {cycle values="#CCCCCC,#999999" assign="row_color"}
    2006-07-25 11:58:59  supandian [Reply | View]

    i only need one color and that is for the most recent record. How do i do it?
    Su
  • Too much overhead...
    2005-03-07 09:20:22  gagari [Reply | View]

    I have been thinking a lot in that problem of integrate PEAR::DB and SmartyTemplate. The approach of the article is interesting in terms of software engeneering, but it doesn´t consider the overhead generated in the process. I mean, when you call a "getAll()" (and similars) it iterates over the DB resource putting the results in an array. Later, when you assign that array to the template it will, again, iterate over the array to fill the data on template. In a small resultset the overhead is insignificant, but in a larger resultset you may be duplicating the execution time. Perhaps it makes sense if between the two blocks of iterations you will be making some kind of validation. However in the most of cases you just want to paste the resultset data on the template.



    I am working on the idea of fetch the resultset directly on the template (maybe using the plugin engine). We could use the proxy design pattern, or something like that, to develop a class that Smarty see as a data container, but as a matter of fact it uses the resultset to retrieve data.

    Would you give some ideas?

    Thanks,
    Iuri

  • Do not use smarty
    2003-12-05 08:18:08  anonymous2 [Reply | View]

    I am currently working on a project with smarty.

    It becomes very complicated.

    Not that you cannot do everything, but you have another level of indirection which complicates things.

    The chain from the caller to the output becomes unnecessarily long.

    Do not use any template system unless you have e.g. a big magazine which needs to change design very often.


    Regards,

    Klaus




  • presentation logic
    2003-12-01 16:06:23  anonymous2 [Reply | View]

    I am just looking at trying smarty as a longtime user of FastTemplates. My complaint with fast templates is you can't nest dynamic templates, and it's slow and the most annoying thing of all is when I have to mix presentation logic with application logic. It makes for pretty ugly code and till now I had no choice.

    Give the designers some credit. If they can understand CSS and html they can understand the designer aspects of smarty I think.
  • Question about HTML structure
    2003-09-17 02:42:37  anonymous2 [Reply | View]

    Hi !

    I see this portion of code as a problem maybe :

    <html><head>
    {if $title != ""}
    <title>website name - {$title}</title>
    {else}
    <title>website name - slogan goes here</title>
    {/if}
    </head><body>

    Why hiding the <title> in the loop ? that will cause uncomprehension to a html editor, no ?
    Could we have stuff like

    <title>website name -
    {if $title != ""}
    {$title}
    {else}
    slogan goes here
    {/if}
    </title>

    I would have done like this, but maybe I'm wrong, as I just start on Templates.

    Thanks anyway for the good article

    Alain (chri01@hotmail.com)
  • Smarty and XML
    2003-08-06 04:09:59  anonymous2 [Reply | View]

    I would like to see an article on creating xml with smarty, I'm trying to do that right now. I'm having problems with repeatings tag within repeating tags, it's hard to abstract this in only templates. I might need to create the tags in PHP.
  • I absolutely agree with author
    2003-05-03 03:13:04  anonymous2 [Reply | View]

    I have to say, it is very different having code snippets in HTML code and having them there not ;-) As I am not a main html editor, just a programmer and I work with HTML only when i really have to. This article is a very good example of separating logic & presentation and i think that developers at Smarty would try to find ways how to make Smarty logic in HTML more understandable or better more invisible for designers...
    I appreciate the article.
  • Default Title
    2003-05-01 09:23:22  anonymous2 [Reply | View]

    I emailed this to jcpm before I saw the discussion at the bottom of the page, so I'll post my comments here. :P In the last example, instead of using {if} and {/if}, you can have just one TITLE tag that looks like this:

    <title>website name - {$title|default:"slogan goes here"}</title>

    I think that makes for much clean code. <shrug>

    jason
  • What do your web pages look like!?!
    2003-04-29 15:48:01  namejko [Reply | View]

    In all honesty I can not believe how thick headed so many people can be! Anyone who thinks that there should be NO PRESENTATION logic in a template file must have a website that is either impossible to update or looks like something designed back in the early 80's!

    With all of the formatting options now available in HTML code, it is almost just as impossible for a programmer to develop HTML code in the "business logic" portion within PHP. A simple example that can extend the row color altering in a table would be one in which a website moves to using different fonts through either font tags or stylesheets. Are you telling me that when the design team decides to add CSS to the fonts in SOME of its tables, it is the programmer's job to go back to EVERY piece of code that involves the outputting a table string and adding that code in ONLY where the design team wants it!?! (before anyone says anything, I understand that you could abstract this somewhat. But like I said...if only a couple of tables are meant to be formatted a certain way, and those tables were all generated using the same abstracted makeTable() code, the development team would need make additional functions with similar code where the ONLY difference is some CSS tags).

    Ultimately, I can't see why designers should not learn minor programming constructs any more than programmers should learn HTML. It is a team effort, but the simple truth is that a designer who learns some Smarty code could make a MUCH nicer web page than a programmer who learns HTML code. Why can't people understand that. Wow!

    Adam
  • Article shows poor understanding of Smarty & pear
    2003-04-23 15:13:42  anonymous2 [Reply | View]

    There are many code examples in this article which are done the wrong way when working with smarty. Here's some of them and the correct code:

    1. subclass Template_API is totally useless. The functions defined in this subclass are dupliacats of existing smarty functions. Instead, the author should have created a smarty object and set the template, compile and config dirs in code rather than in the constructor.

    1a. setTemplate: this function is not necessay. This seems to be a carry-over from phplib type templates where the template engine had to know something about the template before parsing it. Smarty doesn't need to know anything about the template before parsing therefore the smarty function display() is used at the end of a script.

    1b. assign & bulk assign: the smarty function assign() can take an associative array of key=>value pairs and assign them. The inclusing of both of these functions is unnecessary.

    2. This code:
    include_once("config.inc.php");
    include_once(APP_INC_PATH . "class.template.php");
    should NOT be needed for every page. Instead, the config.inc.php should require_once the class.template.php so the duplicate include in every file would be unnecessary.

    3. This code:
    include_once("DB.php");
    $dbh = DB::connect("mysql://user:passwd@server/database");
    is a horrible practice. By putting your userid and password and connection info to your database in every script, you are creating a maintenance nightmare. This should be done in the config.inc.php file.

    4. This code:
    $news = $dbhl->getAll($stmt, DB_FETCHMODE_ASSOC);
    should not include DB_FETCHMODE_ASSOC. Ideal php applications should be designed to use the same fetchmode on every page so the best practice would be to create the db object in config.inc.php and call
    $dbhl->setFetchMode(DB_FETCHMODE_ASSOC);
    then the call in the page would be:
    $dbhl->getAll($stmt);

    5. As another person pointed out in these notes, the use of foreach is prefered over sections. foreach is a newer than section....the author may not be up to speed on the latest smarty versions.

    Tom Anderson
    My own article on the use of pear & smarty:
    http://smarty.incutio.com/?page=SmartestSmartyPractices
  • This is not separating logic from presentation
    2003-04-22 22:22:07  anonymous2 [Reply | View]

    When you still have logic expressions in the HTML, this is not separating logic from presentation.

    If you ask most HTML designers that do not understand your HTML templates because they still have programming logic in them.

    This is a thing that usually developers that work alone have like the article author do not understand because they are used to do both the programming and the presentation design.
  • a cleaner looping syntax
    2003-04-22 06:50:26  anonymous2 [Reply | View]

    FWIW for looping through arrays in Smarty templates I prefer this syntax, which is a bit more readable:

    {foreach from=$widgets item=widget}
    {$widget.name} : {$widget.price}

    {/foreach}

    I enjoyed the tricks relating to bulk assignment of template variables.
  • Um...
    2003-04-21 11:52:41  anonymous2 [Reply | View]

    People's comments about smarty's speed are misleading. If you look at smarty's benchmarks (the ones they programmed), smarty falls somewhere in the middle of the road. If you want speed, smarty simply is not the way to go. It takes an extreme amount of time for php to interpret the huge classes included (which are required EVEN IF the template has already been "compiled" - which seems to make the compiling step pretty pointless). Check their benchmarks, you may be surprised (and those are even with a php script caching solution like phpAccelerator - if you aren't using one, seriously reconsider using smarty).
  • Good code bad article
    2003-04-18 09:17:12  anonymous2 [Reply | View]

    The author assumes we all know what Pear::DB and Smarty are. I've been programming for 20 years and doing PHP for 4 and I didn't know about Smarty. I still don't. A short paragraph on each would have clued those of us who didn't know in while not alienating those who did. I'm suprised O'Reilly let this crap through. They usually have higher standards.


Tagged Articles

Post to del.icio.us

This article has been tagged:

php

Articles that share the tag php:

Understanding MVC in PHP (477 tags)

The PHP Scalability Myth (123 tags)

The Dynamic Duo of PEAR::DB and Smarty (53 tags)

PHP Form Handling (43 tags)

Very Dynamic Web Interfaces (39 tags)

View All

smarty

Articles that share the tag smarty:

The Dynamic Duo of PEAR::DB and Smarty (42 tags)

Introducing Smarty: A PHP Template Engine (6 tags)

Three-Tier Development with PHP 5 (5 tags)

View All

pear

Articles that share the tag pear:

The Dynamic Duo of PEAR::DB and Smarty (37 tags)

Programming eBay Web Services with PHP 5 and Services_Ebay (12 tags)

Three-Tier Development with PHP 5 (6 tags)

PHP's PEAR on Mac OS X (6 tags)

Caching PHP Programs with PEAR (6 tags)

View All

tutorial

Articles that share the tag tutorial:

Rolling with Ruby on Rails (1417 tags)

A Simpler Ajax Path (135 tags)

Ajax on Rails (88 tags)

Rolling with Ruby on Rails, Part 2 (66 tags)

Very Dynamic Web Interfaces (66 tags)

View All

programming

Articles that share the tag programming:

Rolling with Ruby on Rails (1374 tags)

Very Dynamic Web Interfaces (279 tags)

Ajax on Rails (231 tags)

Understanding MVC in PHP (202 tags)

A Simpler Ajax Path (186 tags)

View All

Sponsored Resources

  • Inside Lightroom
Advertisement

Sponsored by:

O'Reilly Media

©2009, O'Reilly Media, Inc.
(707) 827-7000 / (800) 998-9938
All trademarks and registered trademarks appearing on oreilly.com are the property of their respective owners.
About O'Reilly
Academic Solutions
Authors
Contacts
Customer Service
Jobs
Newsletters
O'Reilly Labs
Press Room
Privacy Policy
RSS Feeds
Terms of Service
User Groups
Writing for O'Reilly
Content Archive
Business Technology
Computer Technology
Google
Microsoft
Mobile
Network
Operating System
Digital Photography
Programming
Software
Web
Web Design
More O'Reilly Sites
O'Reilly Radar
Ignite
Tools of Change for Publishing
Digital Media
Inside iPhone
O'Reilly FYI
makezine.com
craftzine.com
hackszine.com
perl.com
xml.com

Partner Sites
InsideRIA
java.net
O'Reilly Insights on Forbes.com