Recipe 18.10. Using a Template System to Generate Web Pages


Problem

Your scripts mix the code for retrieving information from your database with the code for generating HTML. You want to decouple these activities so that they can be performed separately.

Solution

Use a template system that enables you to design the general layout of the page but plug in specific data on a request-specific basis. Then you can obtain the data separately and pass it to the template system for output generation.

Discussion

In this chapter, we've been using an approach that retrieves data for a web page and generates the HTML for the page all in the same script. That has the advantage of simplicity, but there are disadvantages as well, and there are alternatives.

The primary disadvantage of mixing everything together is that there is little functional decoupling (separation of function) between the logic involved in obtaining the data needed for the page (the business or application logic) and the logic that formats the data for display (the presentation logic).

An alternative approach to web page generation is to use some kind of template system that enables functional decomposition of phases of the page-generation process. A template system does this by separating business logic from the presentation logic. If you're partial to the Model-View-Controller (MVC) architecture, templates help you implement MVC logic by taking care of the View part.

There are many template packages to choose from, as a few minutes searching the Internet quickly reveals. Here, we'll briefly explore two (PageTemplate for Ruby and Smarty for PHP), each of which uses the following approach:

  1. The web server invokes a script in response to a client request for a web page.

  2. The script determines what must be displayed and retrieves any data necessary to make that happen.

  3. The script calls the template engine, passing a page template to it along with the data to insert into the template.

  4. The template engine looks for special markers in the template that indicate where to insert data values, replaces them with the appropriate values, and produces the result as its output. The output is the web page that is sent to the client as the response to the client's request.

Typically, template engines are used in web contexts to produce HTML, but depending on your requirements, you could produce other formats such as plain text or XML.

A template system provides the benefits of functional decoupling in the following way:

  • A programmer can change the implementation of how to obtain the information to be displayed without caring about what it will look like when displayed. For example, if you want to change database systems, you can do that in the application logic without requiring changes to any templates (the presentation logic).

  • A page designer can change the way information is displayed by changing the templates, without knowing anything about how to obtain the information. It could come from a file, a database, or whatever. The designer simply assumes that the information is available and doesn't care where it comes from. In particular, the designer doesn't need to know how to write programs.

That last sentence might be considered subject to debate. Although it's true that page designers do not, for the most part, need to know a programming language, it's also true that templates are embedded with special markers that indicate where to substitute data values. Depending on the complexity of the data to be displayed and the markup syntax of the template package, the markup can range from very simple to looking like a program itself! Nevertheless, it's easier to understand template markers than a general-purpose programming language.

In this section, we'll revisit some of the topics that were addressed earlier in the chapter: Generating paragraphs (Section 18.1), lists (Section 18.2), and tables (Section 18.3). But here we'll consider how to create these structures in web pages by designing page templates for PageTemplate and Smarty. Before doing so, the discussion begins for each template package with a short tutorial that shows how to use markup for the following concepts:


Value substitution

How to indicate where to substitute values into a template. A given value can be substituted as is, or it can be subjected to HTML-encoding or URL-encoding first.


Conditional testing

How to select or ignore part of a template based on the result of a test.


Iteration (looping)

How to process part of a template repeatedly, once per element of a data structure such as an array or hash. This is useful for generating lists and tables.

Appendix A indicates where to get PageTemplate and Smarty. I assume in the remainder of this recipe that you have already installed them. The example templates and scripts discussed here can be found under the apache directory of the recipes distribution, in the pagetemplate and smarty subdirectories.

The examples use .tmpl as the extension for template files, but there is no requirement for either template package that you use that particular extension. (For example, Smarty applications often use .tpl.)

Using page template for web page generation in Ruby

A PageTemplate web application consists of an HTML template file for the output page, and a Ruby script that gathers the data needed by the template and calls the template engine to process the template. In PageTemplate, the markup indicators are [% and %]. The content between the markers tells PageTemplate what action to perform.

Value substitution. To indicate where a value goes in a PageTemplate template file, use a [%var var_name %] directive:

[%var myvar%] 

PageTemplate replaces the directive with the value of the var_name variable. By default, the value is substituted as is. To perform HTML-encoding or URL-encoding of the value, add the appropriate preprocessor name to the directive:

[%var myvar :escapeHTML%] [%var myvar :escapeURI%] 

:escapeHTML has the same effect as invoking CGI.escapeHTML⁠(⁠ ⁠ ⁠) to make sure that special characters such as < or & are converted to the corresponding &lt; and &amp; entities. :escapeURI has the same effect as CGI.escape⁠(⁠ ⁠ ⁠).

Any value referred to by a [%var%] directive must be a string, or able to produce a string if the to_s method is applied to it.

Conditional testing. A conditional test begins with an [%if var_name %] directive, ends with [%endif%], and optionally contains an [%else%] directive:

[%if myvar%]   myvar is true [%else%]   myvar is false [%endif%] 

If myvar evaluates to true, PageTemplate processes the content following the [%if%] directive. Otherwise, it processes the content following [%else%]. For a simple conditional with no "else" part, omit the [%else%] part.

Iteration. The [%in var_name %] construct provides iteration over the members of the list named by the given variable. The template content between the [%in%] and [%endin%] directives is processed once per member. If the list members are hashes, you can refer to the hash elements by name. Suppose that mylist is an array consisting of items, each of which is a hash with this structure:

{ "first_name" => value, "last_name" => value } 

The hash members are first_name and last_name, so an iterator can process the list and refer to its item members as follows:

[%in mylist%]   Name: [%var first_name%] [%var last_name%] [%endin%] 

If the list is a simple indexed list, its elements don't have names. In that case, you can provide a name in the [%in%] directive by which to refer to the elements:

[%in mylist: myitem%]   Item: [%var myitem%] [%endin%] 

The following template file, pt_demo.tmpl, demonstrates the concepts just discussed:

<!-- pt_demo.tmpl --> <html> <head> <title>PageTemplate Demonstration</title> </head> <body> <p>Value substitution:</p> <p>   My name is [%var character_name%],   and I am [%var character_role%]. </p> <p>Value substitution with encoding:</p> <p>   HTML-encoding: [%var str_to_encode :escapeHTML%];   URL-encoding: [%var str_to_encode :escapeURI%] </p> <p>Conditional testing:</p> <p>   [%if id%]     You requested information for item number [%var id%].   [%else%]     You didn't choose any item!   [%endif%] </p> <p>Iteration:</p> <p>   Colors of the rainbow:   [%in colors: color%]     [%var color%]   [%endin%] </p> </body> </html> 

We also need a script to go with the template. The basic skeleton of a Ruby script that uses PageTemplate looks like this:

#!/usr/bin/ruby -w # Access required modules require "PageTemplate" require "cgi" ...obtain data to be displayed in the output page... # Create template object pt = PageTemplate.new # Load template file pt.load("template_file_name") # Assign values to template variables pt["var_name1"] = value1 pt["var_name2"] = value2 ... # Generate output (cgi.out adds headers) cgi = CGI.new("html4") cgi.out { pt.output } 

The script begins by including the required Ruby library files: PageTemplate to generate the page, and cgi because its out method provides an easy way to send the page to the client complete with any required headers.

The next steps are to create a template object, load the template file, and associate values with the variables named in the template. The filename should be an absolute variable or a pathname relative to the script's current working directory at the point when the template object is created. The syntax for assigning a value to a template variable is similar to hash-value assignment:

pt["var_name"] = value                

Finally, the script generates output. This is done with the template object's output method. As shown, the script prints output from the template using the cgi object's out method, which takes care of adding the page headers.

We can adapt that skeleton to write a pt_demo.rb script that accompanies the pt_demo.tmpl template file:

#!/usr/bin/ruby -w # pt_demo.rb - PageTemplate demonstration script require "PageTemplate" require "cgi" pt = PageTemplate.new pt.load("pt_demo.tmpl") pt["character_name"] = "Peregrine Pickle" pt["character_role"] = "a young country gentleman" pt["str_to_encode"] = "encoded text: (<>&'\" =;)" pt["id"] = 47846 pt["colors"] = ["red","orange","yellow","green","blue","indigo","violet"] cgi = CGI.new("html4") cgi.out { pt.output } 

To try the application, copy the pt_demo.rb and pt_demo.tmpl files to your Ruby web script directory and then request the script with your web browser. For example, if you copy the files to your usual cgi-bin directory, use this URL to request the script:

http://localhost/cgi-bin/pt_demo.rb 

You might prefer to install the .tmpl file somewhere other than the cgi-bin directory because it isn't an executable file. If you do that, adjust the pathname in the pt.load call.

The preceding tutorial is sufficient background for developing templates to produce paragraphs, lists, and tables that display information retrieved from MySQL.

Paragraph generation. Our first PageTemplate-based MySQL application requires only the production of simple paragraphs. The application is patterned after the discussion in Section 18.1. The scripts developed there connect to the MySQL server, issue a query to retrieve some information, and write out a few paragraphs to report the query result: the current time, the server version, the MySQL username, and the current database. For an equivalent PageTemplate application, we need a template file and a Ruby script, here named pt_paragraphs.tmpl and pt_paragraphs.rb.

Begin by designing the page template. For this application, the template is quite simple. It requires only simple value substitution, not conditionals or iterators. Use the [%var var_name %] construct to make a template for a page containing simple paragraphs:

<!-- pt_paragraphs.tmpl --> <html> <head> <title>[%var title :escapeHTML%]</title> </head> <body bgcolor="white"> <p>Local time on the MySQL server is [%var now :escapeHTML%].</p> <p>The server version is [%var version :escapeHTML%].</p> <p>The current user is [%var user :escapeHTML%].</p> <p>The default database is [%var db :escapeHTML%].</p> </body> </html>

The pt_paragraphs.tmpl template uses :escapeHTML in the [%var%] directives based on the assumption that we don't necessarily have much knowledge about whether the data values contain special characters. That completes the page design.

Next, write the Ruby script that retrieves the data and invokes the template engine. The directives embedded within the template indicate that the accompanying script will need to provide values for template variables named title, now, version, user, and db:

#!/usr/bin/ruby -w # pt_paragraphs.rb - generate HTML paragraphs require "PageTemplate" require "cgi" require "Cookbook" title = "Query Output Display - Paragraphs" dbh = Cookbook.connect # Retrieve data required for template (now, version, user, db) =   dbh.select_one("SELECT NOW(), VERSION(), USER(), DATABASE()") db = "NONE" if db.nil? dbh.disconnect pt = PageTemplate.new pt.load("pt_paragraphs.tmpl") pt["title"] = title pt["now"] = now pt["version"] = version pt["user"] = user pt["db"] = db cgi = CGI.new("html4") cgi.out { pt.output } 

This script is a lot like the pt_demo.rb script discussed earlier. The main differences are that it uses the Cookbook module (for its connect method) and it obtains the data required for the page template by connecting to MySQL and issuing queries.

List generation. Lists contain repeating elements, so they are produced with the PageTemplate [%in%] directive that iterates through a list. The following template generates an ordered list, an unordered list, and a definition list:

<!-- pt_lists.tmpl --> <html> <head> <title>[%var title :escapeHTML%]</title> </head> <body bgcolor="white"> <p>Ordered list:</p> <ol> [%in list: item %]   <li>[%var item :escapeHTML%]</li> [%endin%] </ol> <p>Unordered list:</p> <ul> [%in list: item %]   <li>[%var item :escapeHTML%]</li> [%endin%] </ul> <p>Definition list:</p> <dl> [%in defn_list%]   <dt>[%var note :escapeHTML%]</dt>   <dd>[%var mnemonic :escapeHTML%]</dd> [%endin%] </dl> </body> </html>

The first two lists differ only in the surrounding tags (<ol> versus <ul>), and they use the same data for the list items. The values come from a simple array, so we provide another argument to the [%in%] directive that associates a name (item) with values in the list and gives us a way to refer to them.

For the definition list, each item requires both a term and a definition. We'll assume that the script can provide a list of structured values that have members named note and mnemonic.

The script that retrieves the list data from MySQL and processes the template looks like this:

#!/usr/bin/ruby -w # pt_lists.rb - generate HTML lists require "PageTemplate" require "cgi" require "Cookbook" title = "Query Output Display - Lists" dbh = Cookbook.connect # Fetch items for ordered, unordered lists # (create an array of "scalar" values; the list actually consists of # DBI::Row objects, but for single-column rows, applying the to_s # method to each object results in the column value) stmt = "SELECT item FROM ingredient ORDER BY id" list = dbh.select_all(stmt) # Fetch terms and definitions for a definition list # (create a list of hash values, one hash per row) defn_list = [] stmt = "SELECT note, mnemonic FROM doremi ORDER BY id" dbh.execute(stmt) do |sth|   sth.fetch_hash do |row|     defn_list << row   end end dbh.disconnect pt = PageTemplate.new pt.load("pt_lists.tmpl") pt["title"] = title pt["list"] = list pt["defn_list"] = defn_list cgi = CGI.new("html4") cgi.out { pt.output } 

Recall that for the definition list, the template expects that the list items are structured values containing note and mnemonic members. The script should satisfy this requirement by creating an array of hashes, in which each hash is of this form:

{ "note" => val1, "mnemonic" => val2 } 

This is easily accomplished by selecting the note and mnemonic columns from the table that contains the data, and using the fetch_hash function to retrieve the rows as hashes.

Table generation. Designing a template for an HTML table is similar to designing one for a list because they both have repeating elements and thus use iterators. For a table, the repeating element is the row (using the <tr> element). Within each row, you write the values for the cells using <td> elements. The natural data structure for this is an array of hashes, where each hash has elements keyed by the column names.

The example shown here displays the contents of the cd table, which has three columns: year, artist, and title. Assuming that we use a template variable named rows to hold the table contents, the following template generates the table. Actually, the template generates the table twice (once with all rows the same color, and once with alternating row colors):

<!-- pt_tables.tmpl --> <html> <head> <title>[%var title :escapeHTML%]</title> </head> <body bgcolor="white"> <p>HTML table:</p> <table border="1"> <tr>   <th>Year</th>   <th>Artist</th>   <th>Title</th> </tr> [%in rows%]   <tr>     <td>[%var year :escapeHTML%]</td>     <td>[%var artist :escapeHTML%]</td>     <td>[%var title :escapeHTML%]</td>   </tr> [%endin%] </table> <p>HTML table with rows in alternating colors:</p> <table border="1"> <tr>   <th bgcolor="silver">Year</th>   <th bgcolor="silver">Artist</th>   <th bgcolor="silver">Title</th> </tr> [%in rows%]   [%if __ODD__ %]   <tr bgcolor="white">   [%else%]   <tr bgcolor="silver">   [%endif%]     <td>[%var year :escapeHTML%]</td>     <td>[%var artist :escapeHTML%]</td>     <td>[%var title :escapeHTML%]</td>   </tr> [%endin%] </table> </body> </html> 

The first table template generates a "plain" table. The second template generates a table that has alternating row colors. Switching between colors is easy to do by using a conditional directive that tests the value of the built-in __ODD__ variable that is true for every odd-numbered row.

To fetch the table data and process the template, use this script:

#!/usr/bin/ruby -w # pt_tables.rb - generate HTML tables require "PageTemplate" require "cgi" require "Cookbook" title = "Query Output Display - Tables" dbh = Cookbook.connect # Fetch table rows # (create a list of hash values, one hash per row) rows = [] stmt = "SELECT year, artist, title FROM cd ORDER BY artist, year" dbh.execute(stmt) do |sth|   sth.fetch_hash do |row|     rows << row   end end dbh.disconnect pt = PageTemplate.new pt.load("pt_tables.tmpl") pt["title"] = title pt["rows"] = rows cgi = CGI.new("html4") cgi.out { pt.output } 

Using Smarty for web page generation in PHP

A Smarty web application consists of an HTML template file for the output page, and a PHP script that gathers the data needed by the template and calls the template engine to process the template. In Smarty templates, the markup indicators are { and }, which surround the commands that tell Smarty what actions you want taken.

Value substitution. To indicate where to substitute a value in a Smarty template, use {$ var_name } notation. The substitution uses the value with no preprocessing. If you want to HTML-encode or URL-encode the value, add a modifier. The following three lines substitute a value without modification, with HTML-encoding, and with URL-encoding, respectively:

{$myvar} {$myvar|escape} {$myvar|escape:"url"} 

Conditional testing. A conditional test begins with {if expr }, ends with {/if}, and optionally contains one or more {elseif expr } clauses and an {else} clause. Each expr can be a variable or a more complex expression (unlike PageTemplate, which allows only a variable name). Here is a simple if-then-else test:

{if $myvar}   myvar is true {else}   myvar is false {/if} 

Iteration. Smarty has multiple iterator constructs. The {foreach} command names the variable containing the list of values to iterate over and indicates the name by which the body of the loop will refer to each value. The following construct specifies $mylist as the name of the list and uses myitem for the list item name:

{foreach from=$mylist item=myitem}   {$myitem} {/foreach} 

The {section} command names the variable containing the list of values and the name to use for subscripting the list variable within the loop:

{section loop=$mylist name=myitem}   {$mylist[myitem]} {/section} 

Note that the syntax for referring to list items is different for {section} than for {foreach}. The $mylist[myitem] syntax shown is useful for iterating through a list of scalar values. If you need to iterate through structured values such as associative arrays, refer to structure members as $mylist[myitem]. member_name. The list and table applications discussed later illustrate this syntax further.

The following template file, sm_demo.tmpl, demonstrates the preceding concepts:

<!-- sm_demo.tmpl --> <html> <head> <title>Smarty Demonstration</title> </head> <body> <p>Value substitution:</p> <p>   My name is {$character_name},   and I am {$character_role}. </p> <p>Value substitution with encoding:</p> <p>   HTML-encoding: {$str_to_encode|escape};   URL-encoding: {$str_to_encode|escape:"url"} </p> <p>Conditional testing:</p> <p>   {if $id}     You requested information for item number {$id}.   {else}     You didn't choose any item!   {/if} </p> <p>Iteration:</p> <p>   Colors of the rainbow (using foreach):   {foreach from=$colors item=color}     {$color}   {/foreach} </p> <p>   Colors of the rainbow (using section):   {section loop=$colors name=color}     {$colors[color]}   {/section} </p> </body> </html>

We also need a script to accompany the template. An outline for a PHP script that uses Smarty looks like this:

<?php require_once "Smarty.class.php"; ...obtain data to be displayed in the output page... # Create template object $smarty = new Smarty (); # Assign values to template variables $smarty->assign ("var_name1", value1); $smarty->assign ("var_name2", value2); ... # Process the template to produce output $smarty->display("template_file_name"); ?> 

The Smarty.class.php file gives you access to Smarty's capabilities. As mentioned earlier, I assume that you have Smarty installed already. To make it easy for your PHP scripts to access the Smarty.class.php file without having to specify its full pathname, you should add the directory where that file is located to the value of the include_path configuration variable in your php.ini file. For example, if Smarty.class.php is installed in /usr/local/lib/php/smarty, add that directory to the value of include_path.

The rest of the skeleton shows the essential steps for using Smarty: create a new Smarty template object, specify values for template variables with assign⁠(⁠ ⁠ ⁠), and then pass the template filename to the display⁠(⁠ ⁠ ⁠) object to generate the output.

The skeleton is easily adapted to produce the following script, sm_demo.php, to go along with the sm_demo.tmpl template:

<?php # sm_demo.php - Smarty demonstration script require_once "Smarty.class.php"; $smarty = new Smarty (); $smarty->assign ("character_name", "Peregrine Pickle"); $smarty->assign ("character_role", "a young country gentleman"); $smarty->assign ("str_to_encode", "encoded text: (<>&'\" =;)"); $smarty->assign ("id", 47846); $colors = array ("red","orange","yellow","green","blue","indigo","violet"); $smarty->assign ("colors", $colors); $smarty->display ("sm_demo.tmpl"); ?> 

We now have a simple Smarty application (the template file and the PHP script that uses it), but a bit of additional setup is required before we can deploy the application. Smarty uses a set of directories to do its work, so you'll need to create them first. By default, Smarty assumes that these directories are located in the same directory where the PHP script is installed. The following instructions assume that this directory is mcb under your Apache document root (the same PHP directory that has been used throughout this chapter).

Change location to the mcb directory and create these four directories:

% mkdir cache % mkdir configs % mkdir templates % mkdir templates_c                

The directories you just created must be readable by the web server, and two of them must also be writable. To do this on Unix, set the group permissions appropriately, and then change their group to be that used by the web server. Set the group permissions with these commands:

% chmod g+rx cache configs templates templates_c % chmod g+w cache templates_c                

Next, determine the correct group name to use for the directories. To do this, look in the Apache httpd.conf file for a Group line, which might look like this:

Group www 

That line indicates that Apache runs using the www group ID. Other common values are nobody or apache. Use the group name in the following command, which should be executed as root:

# chgrp www cache configs templates templates_c                

That completes the Smarty directory setup. You can deploy the Smarty demonstration application by copying sm_demo.php to the mcb directory and sm_demo.tmpl to the mcb/templates directory. Then request the script with your web browser:

http://localhost/mcb/sm_demo.php 

For each of the following applications, follow the same principle that the PHP script goes in the mcb directory and the Smarty template goes in mcb/templates.

If you want to keep your application subdirectories outside of your document tree, create them somewhere other than in the mcb directory. In this case, you need to let your PHP scripts know where they are by setting several members of your Smarty object after you create it. For example, if you create the Smarty directories under /usr/local/lib/mcb/smarty, you modify your scripts to tell Smarty about their location as follows:

$smarty = new Smarty (); $smarty->cache_dir = "/usr/local/lib/mcb/smarty/cache"; $smarty->config_dir = "/usr/local/lib/mcb/smarty/configs"; $smarty->template_dir = "/usr/local/lib/mcb/smarty/templates"; $smarty->compile_dir = "/usr/local/lib/mcb/smarty/templates_c"; 

Paragraph generation. The Smarty equivalent to the PageTemplate file shown earlier for generating paragraphs is as follows:

<!-- sm_paragraphs.tmpl --> <html> <head> <title>{$title|escape}</title> </head> <body bgcolor="white"> <p>Local time on the MySQL server is {$now|escape}.</p> <p>The server version is {$version|escape}.</p> <p>The current user is {$user|escape}.</p> <p>The default database is {$db|escape}.</p> </body> </html> 

The template uses nothing more than {$ var_name } value substitution, plus the escape modifier to tell Smarty to perform HTML-escaping on the values.

To retrieve the data referred to in the template and then invoke Smarty, use this script:

<?php # sm_paragraphs.php - generate HTML paragraphs require_once "Smarty.class.php"; require_once "Cookbook.php"; $title = "Query Output Display - Paragraphs"; $conn =& Cookbook::connect (); if (PEAR::isError ($conn))   die ("Cannot connect to server: "        . htmlspecialchars ($conn->getMessage ())); $result =& $conn->query ("SELECT NOW(), VERSION(), USER(), DATABASE()"); if (PEAR::isError ($result))   die (htmlspecialchars ($result->getMessage ())); list ($now, $version, $user, $db) = $result->fetchRow (); $result->free (); if (!isset ($db))   $db = "NONE"; $conn->disconnect (); $smarty = new Smarty (); $smarty->assign ("title", $title); $smarty->assign ("now", $now); $smarty->assign ("version", $version); $smarty->assign ("user", $user); $smarty->assign ("db", $db); $smarty->display ("sm_paragraphs.tmpl"); ?> 

List generation. The following template generates three lists. The first two are ordered and unordered lists that use the same set of scalar item values. The third list is a definition list that requires two values per iteration through a set of items:

<!-- sm_lists.tmpl --> <html> <head> <title>{$title|escape}</title> </head> <body bgcolor="white"> <p>Ordered list:</p> <ol> {foreach from=$list item=cur_item}   <li>{$cur_item|escape}</li> {/foreach} </ol> <p>Unordered list:</p> <ul> {foreach from=$list item=cur_item}   <li>{$cur_item|escape}</li> {/foreach} </ul> <p>Definition list:</p> <dl> {section loop=$defn_list name=cur_item}   <dt>{$defn_list[cur_item].note|escape}</dt>   <dd>{$defn_list[cur_item].mnemonic|escape}</dd> {/section} </dl> </body> </html> 

The definition list uses the {section} command and the $ list [ item ]. member notation mentioned earlier for referring to members of structured values.

The script that processes the template needs to fetch the values for the ordered and unordered lists as an array of scalar values. For the definition list, the script should create an array of associative arrays that have members named note and mnemonic:

<?php # sm_lists.php - generate HTML lists require_once "Smarty.class.php"; require_once "Cookbook.php"; $title = "Query Output Display - Lists"; $conn =& Cookbook::connect (); if (PEAR::isError ($conn))   die ("Cannot connect to server: "        . htmlspecialchars ($conn->getMessage ())); # Fetch items for ordered, unordered lists # (create an array of scalar values) $stmt = "SELECT item FROM ingredient ORDER BY id"; $result =& $conn->query ($stmt); if (PEAR::isError ($result))   die (htmlspecialchars ($result->getMessage ())); $list = array (); while (list ($item) = $result->fetchRow ())   $list[] = $item; $result->free (); # Fetch terms and definitions for a definition list # (create an array of associative arrays) $stmt = "SELECT note, mnemonic FROM doremi ORDER BY id"; $defn_list =& $conn->getAll ($stmt, array (), DB_FETCHMODE_ASSOC); if (PEAR::isError ($defn_list))   die (htmlspecialchars ($result->getMessage ())); $conn->disconnect (); $smarty = new Smarty (); $smarty->assign ("title", $title); $smarty->assign ("list", $list); $smarty->assign ("defn_list", $defn_list); $smarty->display ("sm_lists.tmpl"); ?> 

The script uses getAll⁠(⁠ ⁠ ⁠) to fetch the values for the definition list. This is a connection object method that executes a query and retrieves the result set in a single call. By setting the fetch mode to DB_FETCHMODE_ASSOC, you get an array of associative arrays, each of which has members named note and mnemonicperfect for creating an HTML definition list in Smarty.

Table generation. To generate an HTML table, we need an iterator for looping through the list of rows to produce a <tr> element per row. As with the definition list in the previous application, this is easily done by using a {section} command and referring to the values for cells within the row by using $ list [ item ]. member syntax. The following script uses that syntax to generate two tables (one "plain" table, and one that has alternating row colors):

<!-- sm_tables.tmpl --> <html> <head> <title>{$title|escape}</title> </head> <body bgcolor="white"> <p>HTML table:</p> <table border="1"> <tr>   <th>Year</th>   <th>Artist</th>   <th>Title</th> </tr> {section loop=$rows name=row}   <tr>     <td>{$rows[row].year|escape}</td>     <td>{$rows[row].artist|escape}</td>     <td>{$rows[row].title|escape}</td>   </tr> {/section} </table> <p>HTML table with rows in alternating colors:</p> <table border="1"> <tr>   <th bgcolor="silver">Year</th>   <th bgcolor="silver">Artist</th>   <th bgcolor="silver">Title</th> </tr> {section loop=$rows name=row}   <tr bgcolor="{cycle values="white,silver"}">     <td>{$rows[row].year|escape}</td>     <td>{$rows[row].artist|escape}</td>     <td>{$rows[row].title|escape}</td>   </tr> {/section} </table> </body> </html> 

To produce alternating row colors for the second table, the template uses a {cycle} command, which alternately selects white and silver for successive iterations through the row list. {cycle} provides a nice convenience here for a task that you would otherwise handle using a conditional and the $smarty.section. list .iteration value that returns the current iteration number (beginning with 1):

{if $smarty.section.row.iteration % 2 == 1} <tr bgcolor="white"> {else} <tr bgcolor="silver"> {/if} 

The following script fetches the table rows and processes the template:

<?php # smtables.php - generate HTML tables require_once "Smarty.class.php"; require_once "Cookbook.php"; $title = "Query Output Display - Tables"; $conn =& Cookbook::connect (); if (PEAR::isError ($conn))   die ("Cannot connect to server: "        . htmlspecialchars ($conn->getMessage ())); # Fetch items for table $stmt = "SELECT year, artist, title FROM cd ORDER BY artist, year"; $rows =& $conn->getAll ($stmt, array (), DB_FETCHMODE_ASSOC); if (PEAR::isError ($rows))   die (htmlspecialchars ($result->getMessage ())); $conn->disconnect (); $smarty = new Smarty (); $smarty->assign ("title", $title); $smarty->assign ("rows", $rows); $smarty->display ("sm_tables.tmpl"); ?> 




MySQL Cookbook
MySQL Cookbook
ISBN: 059652708X
EAN: 2147483647
Year: 2004
Pages: 375
Authors: Paul DuBois

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net