(Developer Shed Network) Server Side - PHP - Template-Based Web Development With PatTemplate (Part 2)
(Developer Shed Network) Server Side - PHP - Template-Based Web Development With PatTemplate (Part 2)
Table of Contents
The Advanced Course .........................................................................................................................................1 Scoping It Down..................................................................................................................................................2 Speaking In Tongues ...........................................................................................................................................4 Looping The Loop...............................................................................................................................................6 Legal Eagles.........................................................................................................................................................8 Hide And Seek...................................................................................................................................................10 Setting Things Right.........................................................................................................................................12 Fortune Favours The Brave.............................................................................................................................14 Running On Empty...........................................................................................................................................16 Simple Simon.....................................................................................................................................................18 Brain Dump.......................................................................................................................................................20 A WellFormed Example.................................................................................................................................22 Crash Bang Boom.............................................................................................................................................25 Endgame............................................................................................................................................................27
Scoping It Down
You've already seen how to use the AddVar() method to replace template variables with actual values. However, template variables are "local" to a template, and so, the value assigned to a template variable cannot be accessed from other templates (actually, that's a little white lie which I'm going to recant on the next page, but bear with me for a moment). In the event that you need a variable which is "global", and whose value can be accessed from more than one template, patTemplate offers the addGlobalVar() method call, which makes the value of a template variable available to all other templates. Take a look at the following templates, all of which use the template variable {IMAGES}:
<! app1.tmpl > <patTemplate:tmpl name="main"> <html> <head> <basefont face="Arial"> </head> <body> <table border="1" cellspacing="0" cellpadding="0"> <patTemplate:link src="top" /> <patTemplate:link src="middle" /> <patTemplate:link src="bottom" /> </table> </body> </html> </patTemplate:tmpl> <patTemplate:tmpl name="top"> <tr> <td colspan="3"><img src="{IMAGES}/top.gif"></td> </tr> </patTemplate:tmpl> <patTemplate:tmpl name="middle"> <tr> <td valign="top"><img src="{IMAGES}/left.gif"></td> <td> <form action="login">
Scoping It Down
TemplateBased Web Development With patTemplate (part 2) <table border="0" cellspacing="4" cellpadding="0"> <tr> <td><font size="1">Username:</font></td> <td><input type="text" size="6"></td> </tr> <tr> <td><font size="1">Password:</font></td> <td><input type="password" size="6"></td> </tr> </table> </form> </td> <td valign="top"><img src="{IMAGES}/right.gif"></td> </tr> </patTemplate:tmpl> <patTemplate:tmpl name="bottom"> <tr> <td colspan="3"><img src="{IMAGES}/bottom.gif"></td> </tr> </patTemplate:tmpl>
Now, rather than making three calls to AddVar() one for each of the templates referencing the variable {IMAGES} I can save myself time with a single call to AddGlobalVar(), which makes the value of {IMAGES} available in the global namespace. Here's the script:
<?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("app1.tmpl"); // add global variable $template>AddGlobalVar("IMAGES", "/app1/images"); // parse and display the template $template>displayParsedTemplate("main"); ?>
Scoping It Down
Speaking In Tongues
Related, though in a tangential manner, to the material discussed on the previous page, is the "varscope" attribute. This attribute allows you to import the values of template variables from other templates into the current template, thereby reducing the number of calls you have to make to addVar(). Here's a quick example:
<patTemplate:tmpl name="main" varscope="lang"> Would you like me to speak with you in {LANG}? </patTemplate:tmpl> <patTemplate:tmpl name="lang"> {LANG} </patTemplate:tmpl>
<?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("lang.tmpl"); // define a value for the LANG variable in the "lang" template $template>addVar("lang", "LANG", "Japanese"); // parse and display the "main" template // since this template inherits values from "lang" // the value of LANG in this template will automatically be set $template>displayParsedTemplate("main"); ?>
In this case, I have two templates, "main" and "lang". Both contain references to the template variable {LANG}. However, although the template variable {LANG} has been assigned a value in the "lang" template, no such assignment has been made for the "main" template. Despite this, the "main" template will inherit the correct value from the "lang" template, via the "varscope" attribute specified in its opening tag.
Speaking In Tongues
TemplateBased Web Development With patTemplate (part 2) This is clearly demonstrated in the output of the script above:
Speaking In Tongues
<?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("loop.tmpl"); // parse and display the template $template>displayParsedTemplate("main"); ?>
It It It It It It It It It It It It
is is is is is is is is is is is is
now now now now now now now now now now now now
1 1 1 1 1 1 1 1 1 1 1 1
o'clock o'clock o'clock o'clock o'clock o'clock o'clock o'clock o'clock o'clock o'clock o'clock
Fairly simple, and useful when all you need to do is display a piece of text or markup a specified number of times (you can use the setAttribute() method, discussed a little later, to set the loop counter dynamically). And you can make it even more useful by incorporating the current value of the loop counter within your template like I've done below, with the special {PAT_ROW_VAR} variable:
In this case, the same PHP script will generate slightly different output:
It It It It It It It It It It It It
is is is is is is is is is is is is
now now now now now now now now now now now now
1 o'clock 2 o'clock 3 o'clock 4 o'clock 5 o'clock 6 o'clock 7 o'clock 8 o'clock 9 o'clock 10 o'clock 11 o'clock 12 o'clock
Legal Eagles
You can source an external file into your template via the "src" and "parse" template attributes. Let's suppose I have a copyright notice, stored in the file "copyright.txt", which looks like this:
and tell the engine whether or not to parse the sourced file for template variables with the additional "parse" attribute.
Once that's done, I can call this template from within another template, like this:
<patTemplate:tmpl name="main"> <html> <head> <basefont face="Arial"> </head> <body> This is my Web site. It has lots of interesting stuff on it that you might want to use for your own nefarious purposes. But before you do, read the notice at the bottom of this page. <p> <p> <hr> <patTemplate:link src="copyright" /> </body> </html> </patTemplate:tmpl>
And now, when I parse and display the "main" template, the external file "copyright.txt" will be read and incorporated in the final output by the template engine. Here's what it looks like:
Legal Eagles
Legal Eagles
<patTemplate:tmpl name="main"> I spy, with my little eye... <br> <patTemplate:link src="toaster" /> </patTemplate:tmpl> <patTemplate:tmpl name="toaster" visibility="hidden"> ...a template beginning with T </patTemplate:tmpl>
<?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("toaster.tmpl"); // parse and display the template $template>displayParsedTemplate("main"); ?>
Since the "visibility" attribute of the second template is set to "hidden", it will never be displayed by the template engine. In order to display it, you'll need to turn visibility to "visible" (or just remove the "visibility" attribute altogether):
10
TemplateBased Web Development With patTemplate (part 2) <?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("toaster.tmpl"); // turn visibility on for "toaster" template // comment out the next line to have the template vanish $template>setAttribute("toaster", "visibility", "show"); // parse and display the template $template>displayParsedTemplate("main"); ?>
As you will see, though this might not seem like a big deal right now, it becomes extremely powerful when combined with the ability to programmatically alter the "visibility" attribute on the fly. That's discussed on the next page.
11
<! index.tmpl > <patTemplate:tmpl name="index"> <html> <head><basefont face="Arial"></head> <body> Hello, and welcome to my Web site! <! blah blah > <patTemplate:link src="tip" /> </body> </html> </patTemplate:tmpl> <patTemplate:tmpl name="tip" visibility="hidden"> <p><hr> <font size="1">New user tip: Use the Find box at the top right corner of your screen to quickly search this site.</font> </patTemplate:tmpl>
As you can see, there are two templates in the file above; the second one is initially hidden from view. However, I can turn it on in certain cases such as, for example, when a user visits the site for the first time. Here's the script that takes care of this for me.
<?php // alter this to see how the script functions // when the variable is unset $newUser = true; // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate();
12
TemplateBased Web Development With patTemplate (part 2) // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("index.tmpl"); // turn tips on if new user if ($newUser == true) { $template>setAttribute("tip", "visibility", "visible"); } // parse and display template $template>displayParsedTemplate("index"); ?>
Depending on the value of a particular variable, I can turn the second template on or off, via a call to setAttribute(). You can use the setAttribute() method to manipulate other template attributes as well try it with the "loop" or "varscope" attributes to see how it works.
13
<patTemplate:tmpl name="fortune" type="condition" conditionvar="DAY"> <html> <head> <basefont face="Arial"> </head> <body> And today's fortune is: <br> <patTemplate:sub condition="Mon"> Never make anything simple and efficient when a way can be found to make it complex and wonderful. </patTemplate:sub> <patTemplate:sub condition="Tue"> Life is a game of bridge and you've just been finessed. </patTemplate:sub> <patTemplate:sub condition="Wed"> What sane person could live in this world and not be crazy? </patTemplate:sub> <patTemplate:sub condition="Thu"> Don't get mad, get interest. </patTemplate:sub> <patTemplate:sub condition="Fri"> Just go with the flow control, roll with the crunches, and, when you get a prompt, type like hell. </patTemplate:sub> </body> </html> </patTemplate:tmpl>
14
A little analysis, and you'll see that this isn't as complicated as it looks. The outer "fortune" template has been defined as a conditional template by the addition of two attributes to the <patTemplate:tmpl> tag the "type" attribute, which is set to the value "condition", and the "conditionvar" attribute, which is set to the name of the decision variable to be used during the evaluation process. This conditional template is then broken up into individual subtemplates, enclosed within <patTemplate:sub>...</patTemplate:sub> tags, and each possessing a "condition" attribute. This condition attribute specifies the value of the decision variable that the template engine will use when deciding which subtemplate to display. Here's the other half of the puzzle the PHP script that actually sets a value for the decision variable so that the template engine can select an appropriate subtemplate.
<?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("fortune.tmpl"); $template>AddVar("fortune", "DAY", date("D", mktime())); // parse and display the template $template>displayParsedTemplate("fortune"); ?>
In this case, the PHP script merely sets the value of the template variable {DAY} you'll remember that this is the decision variable defined in the conditional template to the current day of the week, and then displays the parsed template. Internally, the template engine will match the value of {DAY} to the options available in the various subtemplates, and pick the one that fits. As you can see, this is identical to a "switch" statement, or a series of "ifelse" conditional statements and it can come in fairly handy at times.
15
Running On Empty
patTemplate also allows you to cover for the unexpected by specifying two additional subtemplates, one which is displayed when the decision variable cannot be matched, and one to be displayed when the decision variable is empty. Here's an example of how this might work:
<patTemplate:tmpl name="fortune" type="condition" conditionvar="DAY"> <html> <head> <basefont face="Arial"> </head> <body> And today's fortune is: <br> <patTemplate:sub condition="Mon"> Never make anything simple and efficient when a way can be found to make it complex and wonderful. </patTemplate:sub> <patTemplate:sub condition="Tue"> Life is a game of bridge and you've just been finessed. </patTemplate:sub> <patTemplate:sub condition="Wed"> What sane person could live in this world and not be crazy? </patTemplate:sub> <patTemplate:sub condition="Thu"> Don't get mad, get interest. </patTemplate:sub> <patTemplate:sub condition="Fri"> Just go with the flow control, roll with the crunches, and, when you get a prompt, type like hell. </patTemplate:sub> <patTemplate:sub condition="default"> Sorry, closed on the weekend. </patTemplate:sub> <patTemplate:sub condition="empty"> Sorry, cannot determine day of week. Did the world just end? </patTemplate:sub> </body> </html>
Running On Empty
16
In this case, the subtemplate specified as "default" will appear on Saturdays and Sundays, while the subtemplate specified as "empty" will appear if {DAY} is empty. Try it out and see for yourself. You can force patTemplate to look in the global namespace for the value of the decision variable in case it's not available locally, by adding the optional "useglobals" attribute to your template definition.
Running On Empty
17
Simple Simon
In case the standard template type doesn't meet your needs, and the conditional one is too complicated, patTemplate offers you the best of both worlds with its "simpleCondition" template type. This template type defines a list of variables that are required for the template to be displayed; if these variables don't exist, the template will never be displayed. With this in mind, it's possible to simplify and rewrite the example on the previous page as an illustration of the concept:
<patTemplate:tmpl name="fortune" type="simpleCondition" requiredvars="DAY"> <html> <head> <basefont face="Arial"> </head> <body> And today's fortune is: <br> What sane person could live in this world and not be crazy? </body> </html> </patTemplate:tmpl>
<?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("fortune.tmpl"); // comment the next line and the template will never be displayed $template>AddVar("fortune", "DAY", date("D", mktime())); // parse and display the template
Simple Simon
18
Simple Simon
19
Brain Dump
Finally, if you're having problems with the engine, you can use the dump() method to view detailed debugging information on the template engine. Take a look at the following example, and its output:
<?php // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // set name of template file $template>readTemplatesFromFile("music.tmpl"); // assign values to template variables $template>AddVar("footer", "COPYRIGHT", "This material copyright Melonfire, " . date("Y", mktime())); // dump template information $template>dump(); ?>
The dump() method displays information about the available templates in the engine, the values of local and global template variables, a list of template attributes, and a list of unused variables. It provides an easy way Brain Dump 20
TemplateBased Web Development With patTemplate (part 2) to see how the template engine has processed your templates, and to identify and correct errors that may have occurred in your business logic.
Brain Dump
21
A WellFormed Example
Finally, here's a composite example, this one using a conditional template to iteratively build a complete HTML form. The unique thing about this form: the form fields are completely configurable via a userdefined array, with multiple types of forms possible using the same template. First, here are the templates I'll be using:
<! form.tmpl > <! main page > <patTemplate:tmpl name="form"> <html> <head><basefont face="Arial"></head> <body> <form action="processor.php" method="post"> <patTemplate:link src="fields" /> <input type="submit" value="Save"> </form> </body> </html> </patTemplate:tmpl> <! field list > <! conditional template containing subtemplates for each field type > <patTemplate:tmpl name="fields" type="condition" conditionvar="FIELD_TYPE"> <patTemplate:sub condition="text"> {LABEL} <br> <input type="text" name="{NAME}"> <p> </patTemplate:sub> <patTemplate:sub condition="textarea"> {LABEL} <br> <textarea name="{NAME}"></textarea> <p> </patTemplate:sub> <patTemplate:sub condition="password"> {LABEL} <br> <input type="password" name="{NAME}">
A WellFormed Example
22
I've got two templates here: one for the main page and the outer form elements, and one conditional template which uses the {FIELD_TYPE} decision variable to render the form controls. Currently, my template only knows how to handle text fields, password fields and text areas feel free to add more constructs here. Now, I'm going to define a PHP array which will contain information on the fields I'd like to display in my form, together with their labels and names. This array is completely userconfigurable, and may be defined at run time, from a database, configuration file or XML data source. Here's what it looks like:
// field list // in form fieldname => array(fieldtype, fieldlabel) $fields = array( 'fname' => array('text', 'First name'), 'lname' => array('text', 'Last name'), 'address' => array('textarea', 'Address'), 'tel' => array('text', 'Telephone number'), 'email' => array('text', 'Email address') );
Now, all I need is a script to process this array and use it to assign appropriate values to the templates above.
<?php // field list // in form fieldname => array(fieldtype, fieldlabel) $fields = array( 'fname' => array('text', 'First name'), 'lname' => array('text', 'Last name'), 'address' => array('textarea', 'Address'), 'tel' => array('text', 'Telephone number'), 'email' => array('text', 'Email address') ); // include the class include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates");
A WellFormed Example
23
TemplateBased Web Development With patTemplate (part 2) // add templates to the template engine $template>readTemplatesFromFile("form.tmpl"); // get field names as array $keys = array_keys($fields); // iterate throough array foreach ($keys as $k) { // set field type // iteratively build list of form fields $template>addVar("fields", "FIELD_TYPE", $fields[$k][0]); $template>addVar("fields", "NAME", $k); $template>addVar("fields", "LABEL", $fields[$k][1]); $template>parseTemplate("fields", "a"); } // parse and display the template $template>displayParsedTemplate("form"); ?>
In this script, I'm iterating through the array, extracting information on each field name and type, and using that information to parse and render the conditional "fields" template. The "a" parameter to parseTemplate() ensures that the contents of each run are appended to the previous run, thereby iteratively constructing a series of form fields. Finally, the complete set of fields is plugged into the main page and displayed via displayParseTemplate(). Here's what it looks like:
Want a different form? Simply alter the $fields array, and watch in awe as a new form is dynamically generated before your very eyes. You gotta admit, that's pretty cool!
A WellFormed Example
24
<! common.tmpl > <patTemplate:tmpl name="error"> <html> <head><basefont face="Arial"></head> <body> An error occurred. Please contact the <a href="mailto:[email protected]">webmaster</a>. </body> </html> </patTemplate:tmpl> <patTemplate:tmpl name="success"> <html> <head><basefont face="Arial"></head> <body> The operation was successfully executed. </body> </html> </patTemplate:tmpl>
<?php function checkErrors() { global $template, $error; if ($error) { $template>displayParsedTemplate("error"); die; } } function raiseError() { global $error; $error = true; } // include the class
25
TemplateBased Web Development With patTemplate (part 2) include("include/patTemplate.php"); // initialize an object of the class $template = new patTemplate(); // set template location $template>setBasedir("templates"); // add templates to the template engine $template>readTemplatesFromFile("common.tmpl"); // set error variable $error = false; // script starts here // process section 1 // no errors checkErrors(); // process section 2 // let's assume an error occurred raiseError(); checkErrors(); // process section 3 // no errors checkErrors(); // end of script processing // if we get this far, it means no errors // display success code $template>displayParsedTemplate("success"); ?>
In this case, since an error was raised in section two of the script, the checkErrors() function will kill the script and display the error template when it is next invoked. If, on the other hand, no errors are raised during execution of the script, the final call to checkErrors() will have no effect, the line following it will be executed and a success template will be displayed. This is a fairly primitive example, but it does serve to demonstrate how a template engine can assist in creating powerful, flexible error handlers for your Web applications. It works like a charm most of the time not to mention being fairly easy to maintain.
26
Endgame
And that's about it for the moment. In this article, you learned about some of patTemplate's more advanced features, including the ability to assign and use global variables, to dynamically switch templates on and off, and to create conditional templates which mimic the "switch" family of conditional statements. You also put your newfound knowledge to the test with a couple of reallife examples, using the template engine to dynamically generate Web forms and to gracefully recover from errors in script execution. That's about it for this tutorial. I hope you enjoyed it, that you learned something useful from it, and that it encouraged you to look at patTemplate as a viable, more efficient alternative to the traditional way of constructing a PHP Web application. Be good, and I'll see you soon! Note: All examples in this article have been tested on Linux/i586 with Apache 1.3.20 and PHP 4.1.0. Examples are illustrative only, and are not meant for a production environment. Melonfire provides no warranties or support for the source code described in this article. YMMV!
Endgame
27