13.2. Templates
Separating code from HTML can be difficult
in PHP. As we discussed in Chapter 1 and have
shown throughout this book, one of the best features of PHP is that
scripts can be embedded anywhere in HTML documents. However, this can
lead to maintenance problems: if we want to redesign the presentation
of the web site, then we may need to rewrite code or, at the very
least, understand how PHP and HTML are interleaved in the
application. This also makes it difficult to maintain code when it is
interleaved with presentational components.
A good solution for medium- to large-scale web database applications
is to use templates to separate markup and code.
In this section, we illustrate how templates can be used in PHP
applications through a case study example from the online winestore.
In our example, we use the open source
XTemplate class library available from
http://sourceforge.net/projects/xtpl/. The
XTemplate library is object-based, and Chapter 2
provides a brief introduction to the object-oriented features of PHP.
There are other excellent template libraries, including most notably
the Smarty PHP template
engine
available from http://www.phpinsider.com/php/code/Smarty/.
13.2.1. Templates in the Shipping Module
Example 13-1 and
Example 13-2 show a template module that displays the
order receipt. This script, called shipping.3,
is a replacement for the shipping.2 script
discussed in Chapter 12. The output of retrieving
Example 13-2 with a Netscape web browser is shown in
Figure 13-1. Example 13-1 is the
application logic, and Example 13-2 is the template.
Example 13-1. shipping.3 provides an order receipt
<?php
include "xtpl.p";
include "include.inc" ;
set_error_handler("errorHandler");
function show_HTML_receipt($custID, $orderID, $connection)
{
// Create a new XTemplate object called $xtpl
$xtpl= new XTemplate ("example.shipping.3.xtpl");
// Find customer information
$query = "SELECT *
FROM customer
WHERE cust_id = $custID";
if (!($result = @ mysql_query ($query, $connection)))
showerror( );
// There is only one matching row
$row = @ mysql_fetch_array($result);
// Assign the orderId to the template
$xtpl->assign("ORDER_ID", $orderID);
// Assign the customer data to the template
$xtpl->assign("CUSTOMER", $row);
// Parse the template data
$xtpl->parse("main.customer");
$orderTotalPrice = 0;
// list the particulars of each item in the order
$query = "SELECT i.qty, w.wine_name, i.price,
w.wine_id, w.year, wi.winery_name
FROM items i, wine w, winery wi
WHERE i.cust_id = $custID
AND i.order_id = $orderID
AND i.wine_id = w.wine_id
AND w.winery_id = wi.winery_id
ORDER BY item_id";
if (!($result = @ mysql_query ($query, $connection)))
showerror( );
// Add each item to the email
while ($row = @ mysql_fetch_array($result))
{
// Work out the cost of this line item
$itemsPrice = $row["qty"] * $row["price"];
$orderTotalPrice += $itemsPrice;
$wineDetail = showWine($row["wine_id"], $connection);
// Assign the qty, wine details, price, and
// total item cost to the template
$xtpl->assign("QTY", $row["qty"]);
$xtpl->assign("WINE", $wineDetail);
$xtpl->assign("PRICE",
sprintf("%-.2f", $row["price"]));
$xtpl->assign("TOTAL",
sprintf("%-.2f", $itemsPrice));
// Parse a template row of items
$xtpl->parse("main.items.row");
}
// Assign the order total to the template
$xtpl->assign("ORDER_TOTAL",
sprintf("%-.2f", $orderTotalPrice));
// parse all items
$xtpl->parse("main.items");
// parse the whole document
$xtpl->parse("main");
// output the templated data
$xtpl->out("main");
}
// Main ----------
// Re-establish the existing session
session_start( );
// Check if the user is logged in
if (!session_is_registered("loginUsername"))
{
session_register("message");
$message = "You must login to view your receipt.";
// Redirect the browser back to the login page
header("Location: example.order.1.php");
exit;
}
// Check the correct parameters have been passed
// unless the script is run correctly
if (!isset($custID) || !isset($orderID))
{
session_register("message");
$message = "Incorrect parameters to " .
"example.shipping.3.php";
header("Location: $HTTP_REFERER");
exit;
}
// Check this customer matches the custID
if ($custID != getCustomerID($loginUsername, NULL))
{
session_register("message");
$message = "You can only view your own receipts!";
header("Location: example.order.1.php");
exit;
}
// Open a connection to the DBMS
if (!($connection = @ mysql_pconnect($hostName,
$username,
$password)))
showerror( );
if (!mysql_select_db($databaseName, $connection))
showerror( );
// Show the confirmation HTML page
show_HTML_receipt($custID, $orderID, $connection);
?>
13.2.1.1. The application logic
Example 13-1 is the application logic that produces the
order receipt. The script logic is identical to that of the
shipping.2 script discussed in Chapter 12. The different features of this script are the
omission of any code to produce output, and the inclusion of the
fragments of code that assign database values to presentation
elements, parse these elements, and call functions in the template
class library.
The script in Example 13-1 works as follows:
-
Include the xtpl.p template library.
-
In the function show_HTML_receipt(
), associate the script with the template shown
in Example 13-2:
$xtpl= new XTemplate ("example.shipping.3.xtpl");
This creates a new template object called $xtpl.
-
Query the customer table and assign the returned
$row to an element of the template named
CUSTOMER. Also assign the
orderID session variable to an element of the
template named ORDER_ID. Uppercase strings are
used to distinguish template elements from variables in the script,
but this isn't essential. After assigning the data,
parse the main.customer template data (we discuss
the structure of the template later in this section).
-
Retrieve and assign each item in the order to the template elements
QTY, WINE,
PRICE, and TOTAL. Now check
that this data is correctly formed and associated by parsing it. The
following lines perform these functions:
// Assign the qty, wine details, price, and total item
// cost to the template
$xtpl->assign("QTY", $row["qty"]);
$xtpl->assign("WINE", $wineDetail);
$xtpl->assign("PRICE", sprintf("%-.2f", $row["price"]));
$xtpl->assign("TOTAL", sprintf("%-.2f", $itemsPrice));
// Parse a template row of items
$xtpl->parse("main.items.row");
We explain how this relates to the template later.
-
Assign the overall order total to the template and check the overall
structure of the data. The final check includes parsing the
items and the overall main
output, and then outputting the data with the following code:
$xtpl->assign("ORDER_TOTAL",
sprintf("%-.2f", $orderTotalPrice));
// parse all items
$xtpl->parse("main.items");
// parse the whole document
$xtpl->parse("main");
// output the templated data
$xtpl->out("main");
13.2.1.2. The template
Example 13-2 is the template itself. It consists
mostly of HTML but also contains a Cascading Style Sheet for
presentation. Cascading
Style Sheets (CSS) are used to control the styles that present
output; references that discuss the CSS standard are listed in
Appendix E.
Interleaved throughout the template are the following HTML comments:
<!-- BEGIN: main -->
<!-- BEGIN: customer -->
<!-- END: customer -->
<!-- BEGIN: items -->
<!-- BEGIN: row -->
<!-- END: row -->
<!-- END: items -->
<!-- END: main -->
These comments describe the structural elements referenced in the PHP
script in Example 13-1. The elements are nested, so
that the element customer is a child of
main, because <!-- BEGIN: customer
--> occurs inside the <!-- BEGIN:
main --> and <!-- END:
main --> tags. The customer element
can therefore be referenced in Example 13-1 as
main.customer. A table row can
be referenced in Example 13-1 as
main.items.row, because the structural element
row is inside items, and
items is inside main.
Consider now how the template produces row data:
<!-- BEGIN: row -->
<tr>
<td>{QTY}</td>
<td>{WINE}</td>
<td align="right">$ {PRICE}</td>
<td align="right">$ {TOTAL}</td>
</tr>
<!-- END: row -->
The tags {QTY}, {WINE},
{PRICE}, and {TOTAL} represent
where data is inserted. The data is assigned to these tags in Example 13-1 using the assign(
)
function. For example, {QTY} is the position where
the order item quantities from the database appear. If there are two
database rows assigned to main.items.row elements,
two <tr> rows are produced as the body of
the HTML <table>.
Example 13-2. HTML XTemplate used by Example 13-1 to output order receipts
<!-- BEGIN: main -->
<!DOCTYPE HTML PUBLIC
"-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html401/loose.dtd">
<html>
<head>
<title>Hugh and Dave's Online Wines</title>
<style type="text/css">
body {background: #ffffff;
color: #000000;
font-family: Arial, sans-serif}
h1 {font: arial}
h2 {font: arial;
font-size: 22}
a {color: #0000ff;
font: helvetica;
font-weight: bold;
text-decoration: none}
</style>
</head>
<body bgcolor="white">
<!-- BEGIN: customer -->
<h1>Your order (reference # {CUSTOMER.cust_id} - {ORDER_ID})
has been dispatched</h1>
Thank you {CUSTOMER.title} {CUSTOMER.surname},
your order has been completed and dispatched.
Your order reference number is
{CUSTOMER.cust_id} - {ORDER_ID}.
Please quote this number in any correspondence.
<br>
<p>If it existed, the order would have been shipped to:
<br><b>
{CUSTOMER.title} {CUSTOMER.firstname} {CUSTOMER.initial}
{CUSTOMER.surname}
<br>
{CUSTOMER.addressline1}
{CUSTOMER.addressline2}
{CUSTOMER.addressline3}
<br>{CUSTOMER.city} {CUSTOMER.state} {CUSTOMER.zipcode}
<br>{CUSTOMER.country}
</b>
<br>
<br>
<p>We have billed your fictional credit card.
<!-- END: customer -->
<!-- BEGIN: items -->
<table border=0 width=50% cellpadding=0 cellspacing=5>
<tr>
<td><b>Quantity</b></td>
<td><b>Wine</b></td>
<td align=\"right\"><b>Unit Price</b></td>
<td align=\"right\"><b>Total</b></td>
</tr>
<!-- BEGIN: row -->
<tr>
<td>{QTY}</td>
<td>{WINE}</td>
<td align="right">$ {PRICE}</td>
<td align="right">$ {TOTAL}</td>
</tr>
<!-- END: row -->
<tr></tr>
<tr>
<td colspan=2 align="left"><i>
<b>Total of this order</b></td>
<td></td>
<td align="right">$<b><i>{ORDER_TOTAL}</b></td>
</tr>
</table>
<!-- END: items -->
<p><i>An email confirmation has been sent to you.
Thank you for shopping at Hugh and Dave's Online Wines.</i>
<form action="example.cart.5.php" method="GET">
<table>
<tr>
<td><input type="submit" name="home" value="Home"></td>
</tr>
</table>
</form>
<br><a href="http://validator.w3.org/check/referer">
<img src="http://www.w3.org/Icons/valid-html401"
height="31" width="88" align="right" border="0"
alt="Valid HTML 4.01!"></a>
</body>
</html>
<!-- END: main -->
The customer details are output by accessing the
CUSTOMER element. For example, the shipping
details are produced with the fragment:
<p>If it existed, the order would have been shipped to:
<br><b>
{CUSTOMER.title} {CUSTOMER.firstname} {CUSTOMER.initial}
{CUSTOMER.surname}
<br>
{CUSTOMER.addressline1}
{CUSTOMER.addressline2}
{CUSTOMER.addressline3}
<br>{CUSTOMER.city} {CUSTOMER.state} {CUSTOMER.zipcode}
<br>{CUSTOMER.country}
</b>
The overall result of running Example 13-1 and using
the template in Example 13-2 is the output in Figure 13-1. The advantage of this approach is that the
HTML in Example 13-2 can be altered independently of
the script in Example 13-1 and vice versa. This means
that application logic and presentation are as separate as possible.
The only links between the two are the structural markup components
and the embedded tags.
Figure 13-1. Output of Examples 13-1 and 13-2
The XTemplate library has more complex features not discussed here.
More details on the use of templates can be found at the web sites
listed earlier in this section.
 |  |  | | 13. Related Topics |  | 13.3. Searching and Browsing |
Copyright © 2003 O'Reilly & Associates. All rights reserved.
|