13.2. TemplatesSeparating 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 ModuleExample 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 logicExample 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:
13.2.1.2. The templateExample 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-2The 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. Copyright © 2003 O'Reilly & Associates. All rights reserved. |
|