Another way of putting it would be that a lot of different programming languages andenvironments, including PHP, have tools available for parsing and generating XML.Therefore, XML can be
Trang 1the PHPTAL equivalent would be
<td tal:content="username">Dummy user name</td>
PHPTAL is supremely friendly from a web designer’s point of view WYSIWYG HTMLediting tools generally ignore unknown attributes, and PHPTAL lets you insert dummycontent that will make the template look like the real web page when viewed in a WYSI- WYG HTML editor—or in a web browser for that matter Figure 13.4 shows what aPHPTAL template for the user list looks like when opened as a file in a web browser
This is possible because of PHPTAL’s ability to insert example content that disappearswhen the real application is run Let’s look at the PHPTAL template (see listing 13.5)
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
Figure 13.4 PHPTAL template viewed in web browser
Listing 13.5 PHPTAL template for user list
b
Trang 2<td tal:content="user/getUsername">victor</td>
<td tal:content="user/getFirstname">Victor</td>
<td tal:content="user/getLastname">Ploctor</td>
<td tal:content="user/getEmail">victor@example.com</td> <td tal:content="user/getRole">regular</td>
<td> <a href="userform.php?id=$user/getID" f class="CommandLink">Edit</a> </td> </tr> <tr tal:replace="">
<td>elietta</td>
<td>Elietta</td>
<td>Floon</td>
<td>elietta@example.com</td>
<td>webmaster</td>
<td>
<a href="userform.php?id=42"
class="CommandLink">Edit</a> </td>
</tr>
</table>
</div>
</body>
</html>
b The DOCTYPE declaration was generated by HTML Tidy It expresses the fact that this is an XHTML document PHPTAL will be just as happy if we replace it with a plain XML declaration such as <? xml version="1.0"> In fact, PHPTAL seems
to accept the file without the declaration, but it’s better to have a file that can be checked by an XML parser
C The xmlns attribute, generated by Tidy, declares the XHTML namespace to be the default namespace for this document That means that all tags and attributes without
an explicit namespace should be interpreted as belonging to the XHTML namespace
D The tal:repeat attribute is technically an XML attribute belonging to the TAL namespace The namespace is a way of making sure an attribute is distinct from all other attributes This makes it possible for us to use another repeat attribute from another namespace if we should happen to need it
What tal:repeat does in PHPTAL may be obvious: it iterates over the array of user objects in exactly the same way that foreach in PHP or Smarty does The dif-ference is that because tal:repeat is an attribute, we don’t need to place a separate tag for it Nor do we need an end tag; the </tr> tag is the end tag for tal:repeat
E tal:content replaces everything between the tags with the content taken from our user object So the user name and other data inside the tags is only dummy or
e
g
Trang 3example content that makes the template easier to understand and to view in a WYG editor.
WYSI-No escaping is required, since PHPTAL does this by default
F Most of the dynamic content in PHPTAL templates is represented as TAL attributes
To add content to an attribute, it’s more intuitive to use a different syntax, which iswhat you can see here To insert the user ID into the href attribute, we represent it
as $user/getID Again, the template is as close to the plain HTML representation
as possible
G Because of the tal:replace attribute, this entire table row is thrown out—replaced with an empty string—when the template is processed The first table row inthe template—the one that contains tal:repeat—generates all the table rows inthe output The dummy row is only there for the sake of the template: it makes thetemplate resemble the web page that’s generated when the application is run We canadd any number of such dummy rows if we want They will all disappear when thetemplate is processed
The difference between tal:replace and tal:content is the following:tal:content removes the material between the HTML tags and replaces it withdynamic content tal:replace removes what’s between the tags and the tags
we just echo the results of template processing
One of the advantages of PHPTAL is that the templates are XML and can be cessed using other XML-based tools This is even more applicable to the next item onour agenda: XSLT
pro-Listing 13.6 Processing the PHPTAL template
Trang 413.3 T RANSFORMATION : XSLT
XSLT stylesheets are another popular way of expressing the HTML tag content of aweb page XSLT, the XML stylesheet language, is a way of transforming XML docu-ments into HTML documents or into other XML documents So if we want to useXSLT as templates in a PHP application, we first generate XML code, transform thatusing XSLT, and output it to the browser Figure 13.5 shows how XSLT works whengenerating web pages The stylesheet can be similar to the templates we’ve seenbefore, but is officially a recipe for the transformation of the XML file into HTML.XSLT is very different from the other template systems It’s a powerful, non-pro-cedural programming language You can do all sorts of advanced things with it Butit’s not necessarily the answer to all your template prayers Its main advantage is its sta-tus as a cross-platform standard Martin Fowler says:
You can use XSLT to transform XML created from J2EE or NET , which can help in putting a common HTML view on data from different sources.
Another way of putting it would be that a lot of different programming languages andenvironments, including PHP, have tools available for parsing and generating XML.Therefore, XML can be used to communicate between these languages and environ-ments, and XSLT is a natural tool to use when you already have data in XML format.Fowler also thinks that XSLT makes it “easier to keep the transform focused only
on rendering HTML, thus avoiding having too much logic in the view.” My experience
is exactly the opposite: XSLT offers such interesting opportunities for implementingview logic that the temptation may be hard to resist
Figure 13.5 XSLT works by transforming an XML data file using an XSL stylesheet.
Trang 513.3.1 “XMLizing” a web page
When you want to produce an XSLT stylesheet from an existing PHP file or from asample HTML file, the first thing to do is to create something that’s valid XML Oneway to do this is the following:
• Replace the PHP processing instructions (<?php ?>) with somethingthat an XML parser will take to be a plain string For example, you can replace
<? with [ and ?> with ]
• Run HTML Tidy to generate valid XML (XHTML)
HTML Tidy is a utility program that helps clean up HTML and convert it intoXHTML It’s available at http://tidy.sourceforge.net There is also a PHP Tidy exten-sion But for our current purposes, the command-line utility is fine A typical way torun it would be as follows:
tidy -indent -asxml -wrap 150 userlist.xhtml
-indent produces indented output -asxml specifies that the output should beXML -wrap 150 makes Tidy wrap lines at 150 characters rather than the default
68 With very complex web pages, this may be helpful, since they will sometimes be
so deeply indented that there is little room left on the line
Tidy sometimes only gives warnings At other times, it reports fatal errors thatrequire you to change the file manually For instance, browsers are usually willing torender a web page even if table markup is incorrectly and inconsistently placed Tidy(not to mention XML parsers) is not so forgiving
After using Tidy, you can test the result using an XML parser such as the commandline utility called xmllint It’s part of libxml2, the Gnome XML toolkit The XMLsupport in PHP 5 is based on libxml2 It’s included in several Linux distributionsand is also available for Windows
The command-line XSLT tool for libxml2 is called xsltproc You can run it as follows:
$ xsltproc userlist.xsl userlist.xml
Trang 6You can generate the XML test file or write it manually It’s typically a very simplerepresentation of the data from the database Listing 13.7 shows how the user listmay be represented.
Listing 13.7 XML file for testing XSLT template processing
Listing 13.8 XSLT stylesheet for the user list
b
Trang 7b Yes, you have to insert all this stuff just to get a valid XSL stylesheet XSL is verbose.
C The output method is html, as we want to generate HTML code
D xsl:template is where the real XSLT processing starts The match expression is anXPath expression capable of matching a node or a set of nodes in the input XML doc-ument The template is processed whenever XSLT encounters a node that matches Inthis case, the template matches the root node Since processing starts at the root node,XSLT will start on this template immediately And since there are no other templates,processing this one is all it will do
E All namespaces have to be declared This goes for the html namespace as well
F The xsl:for-each selects all the user elements and tells XSLT to process each one.From an XSLT-purist point of view, this is sinful: using xslt:for-each in thiscontext is not idiomatic in XSLT XSLT is a non-procedural programming language,and for-each is a procedural mode of expression, foreign to XSLT The typical way
to do it in XSLT would be to use a separate template for the repeating section The son for using for-each is not to make it cozy and familiar for PHP programmers.Instead, the intention is to make a single template that will resemble an HTML page
rea-g
Trang 8G xsl:value-of is the XSLT equivalent of echo or print in PHP Again, the select expression is an XPath expression The expression is interpreted relative to the current node, so while XSLT is processing one of the user nodes, it outputs the content of, say, the username element in that user node
H The expression that defines the link URL may be an ugly brute, but it’s more HTML -like than some alternatives The outer braces mean that what’s inside is an XPath expression instead of a string The concat() function is simple string concatena-tion In this case, it concatenates the literal string userform.php?id= with the result of the XPath expression id, which happens to be the user ID
13.3.4 Running XSLT from PHP
Although the functions needed to run XSLT from PHP are documented in the PHP manual, we’ll see them in context using the user list again for a complete example First we generate the XML code and then we transform it (see listing 13.9)
$finder = new UserFinder;
$users = $finder->findAll();
ob_start(); b
?>
<?php echo '<?xml version="1.0" ?>'."\n"; ?>
<userlist>
<?php foreach ($users as $u) : ?>
<user>
<username>
<?php echo htmlentities($u->getUserName()) ?>
</username>
<firstname>
<?php echo htmlentities($u->getFirstName()) ?>
</firstname>
<lastname>
<?php echo htmlentities($u->getLastName()) ?>
</lastname>
<email><?php echo htmlentities($u->getEmail()) ?></email> <role><?php echo htmlentities($u->getRole()) ?></role>
<id><?php echo htmlentities($u->getID()) ?></id>
</user>
<?php endforeach; ?>
</userlist>
<?php $xml = ob_get_contents(); ob_end_clean();
print processXslt($xml,'userlist.xsl'); e function processXslt($xml,$xslfile) { $dom = new DomDocument;
$dom->loadXML($xml);
Listing 13.9 Generating the user list with XSLT
c
d
f
Trang 9$xsldom = new domDocument();
C The XML section is basically a simplified version of an HTML section in a PHP file.All presentation-related elements have been stripped away, and all that’s left is adata structure
Again, we are escaping all the data This is to be processed through XSLT, and XSLTwill usually ignore HTML tags, so the risk of cross-site scripting attacks is less It’s morelikely that suspicious content could generate a fatal syntax error, and using htmlen-tities() helps prevent that
D We get the buffered XML code and turn off output buffering
E The XSLT processing is packaged into a function that takes XML text and the name ofthe stylesheet file as arguments
F Create a DOM based on the XML document in $xml
G Create another DOM based on the stylesheet We read this from a file instead of astring, since we have the XML code as a string and the XSLT stylesheet in a file That’snatural since the stylesheet is relatively constant, while the XML code contains data-base data that may change at any time
H Instantiate an XSLT processor Tell the XSLT processor to use the stylesheet sented by our second DOM Then transform the XML using the XSL stylesheet We’ve seen how to use template engines based on various principles Using templatesgoes a long way toward achieving separation between HTML and PHP code Butthere is still the risk that we will start undermining the separation by adding toomuch programming logic to the template itself, either by using the template engine’sbuilt-in programming capabilities (XSLT has a lot of that), or by sneaking in signifi-cant amounts of PHP code (Smarty and PHTAL both have that option) In the nextsection, we’ll study some tricks that will help us resist that temptation in particularlydifficult cases
Most web application pages have a relatively simple structure, such as a form or a ple list A loop and perhaps a few simple conditionals will suffice as logic for the
sim-g h
Trang 10presentation That’s no big problem in a template, since this minimal logic doesn’tobscure the HTML layout of the page much.
But there are a few challenges that are harder to manage without putting more logicinto the templates Presentation logic is a gray area between domain logic and pure lay-out and design: logic that only determines how the data is presented on the web pagebut is still program logic
These are the cases in which presentation logic gets more complex An example that
is often cited is alternating colors for table rows There is no way (currently) to do thiswith HTML or CSS only (It should be possible with the nth-child() pseudo-class
in CSS 3, but browser support for this is practically nonexistent at this writing It’s alsopossible with the JavaScript DOM.)
Unless the template engine has a special feature that will help us with it, we needsomething like an if test embedded in a loop That makes the logic in the templatemore complex and harder to read and manage
Template designers can live with looping and simple conditionals But when youstart to get nested loops and complex conditionals, they find it at best annoyingbecause it gets in the way of their work At worst, it’s confusing to designers and opensthe door to the dreaded tangle of HTML markup and program code
In this section, we’ll first deal with a general pattern (View Helper) for handlinglogic that is part of the Presentation layer but is too complex to fit comfortably in atemplate Then we’ll look at a series of real-life situations that challenge our ability tokeep program logic out of templates and suggest a solution to each of these situations
13.4.1 View Helper
A common strategy for dealing with this is to put presentation logic in PHP classesoutside the template (see figure 13.6) We can keep them in separate classes that onlyhandle the View and do not touch the Domain layer These classes should not gener-ate any HTML code, but they can generate presentation-related information such asthe depth of an item in a hierarchical display or CSS classes to allow alternating rowcolors in a table
This is often considered a form of the View Helper design pattern View Helper
is a somewhat vague concept But in this context, it has a specific responsibility: to
Figure 13.6 Presentation logic can be han- dled by a specialized view class
Trang 11convert or translate the information in domain and data objects into a form that can
be used in a template
This approach makes it possible to use a very simple template language In fact, youcould probably make your own with little effort Simple variable substitution, condi-tionals, and loops should be sufficient
13.4.2 Alternating row colors
Alternating colors in the rows of a table is a popular way to make it easier to distinguishthe rows in the browser You might think this needs to be implemented with program-ming logic in the template because a designer might need to change it The colorsmight change, or the designer might decide not to have alternating colors after all.Some template engines have facilities that make this easier For example, Smartyhas a function called cycle that lets you alternate automatically between a set of val-ues The alternative, which will work with any template engine, is to do the alternationlogic in PHP before passing the values to the template
We definitely want to avoid having explicit color names or codes in the PHP gram code We don’t want to have to change a PHP file for the sake of a styling change.The way to do it is to generate a table with alternating CSS classes for the rows Thenthe colors can be defined in CSS, and the only thing that’s left for PHP is the abstractalternation logic The HTML code would look like this example:
13.4.3 Handling date and time formats
Date and time formats are another challenge when we try to separate the mer’s job from the web designer’s The choice of format is purely a presentation issue;
Trang 12program-there is no reason why it should depend on technical considerations But, as withalternating colors, it has no native syntax in HTML and/or CSS So ideally, we shouldprovide the web designer with a way to specify the format inside the template (There
is an exception to this: if we know there is only one date format we ever want to use,
we can just generate it in the PHP code.)
One way to do this is to use a modifier Smarty has a built-in variable modifiercalled date_format that allows a designer to specify a date format using strf-time() syntax:
{$smarty.now|date_format:"%H:%M:%S"}
But it would be less cryptic and probably more practical if the date format had aname A web site will probably be using only a few different date formats that areused repeatedly on different web pages So having two or three named date formatswould make them easier to remember and make it possible to change a date formatglobally For example, we might have a standard date and time format, one format forjust the time, and a short format for cramped spaces on the page
If the template engine has the ability to define custom modifiers, you could use that
to define named date formats But a solution which is more general—more dent of which template engine you’re using—is to give the template a PHP object whichhas a method to generate the appropriate date format For some reason, objects that rep-resent date and time have not been common in PHP, but they’re useful for this kind oftask Listing 13.10 shows a simplified class resembling the examples in chapter 8
b The DateAndTime object is constructed from a specified timestamp If no timestamp
is specified, the object represents the current time when it was created
c The isoformat() and rfcformat() methods return the formatted date and time
as a string
Listing 13.10 A simplified date and time class
c
Trang 13So we could use the object like this:
$now = new DateAndTime;
echo $now->isoformat()."\n";
This is interesting, but the real practical value starts to appear when we use the AndTime object to replace other ways of representing the date and time.Listing 13.11 shows a class representing a DiscussionMessage object that contains theknowledge of when it was created
function isotime() { return $this->created->isoformat(); }
function rfctime() { return $this->created->rfcformat(); }
function getSubject() { return $this->subject; }
function getText() { return $this->text; }
function getCreated() { return $this->created; }
}
b To make sure we construct the object correctly, let’s use a type hint to require that the
$created argument is already a DateAndTime object Using a type hint is larly appropriate in this case It’s easy to make a mistake and use an integer time-stamp, and the mistake won’t become apparent during construction
particu-C The isotime() and rfctime() methods just call the corresponding methods inthe DateAndTime objects They are not strictly needed if we have a convenient way
to call a method on the DateAndTime object itself Since we’re using it in a template,that depends on the template engine
The DiscussionMessage class can be used like this in PHP 5 code:
$message = new DiscussionMessage(
Trang 14Since a template engine won’t necessarily let us do the equivalent of that last line, it’sconvenient to be able to do this instead:
13.4.4 Generating hierarchical displays
Threaded discussion forums—such as the one in figure 13.7—are good examples ofhierarchical data to display on a web page
An object-oriented tree structure is useful for processing this type of data But howcan we insert it into a template? The problem is that we don’t know how many levels
of replies we need to handle So even a very complex (and not very readable) set ofnested loops is inadequate for the task
Figure 13.7 A threaded discussion view
Trang 15Recursion is the normal way to process tree
structures So one possibility is to give the
tem-plate engine the ability to do recursion This
will still not be very readable, nor will it be easy
to test Another way to do it is to simplify the
data structure by first transforming it into a
simple two-dimensional array or an array of
objects To show the threaded list using HTML,
we need to insert it into rows and columns
any-way Figure 13.8 depicts this process A tree,
identical in structure to the one implied by figure 13.7, is transformed into an array
By using a separate View-oriented class to do this, we can maintain the separationbetween presentation and domain logic while keeping the template from containingmuch more than the usual presentation logic The class in listing 13.12 does the job
of generating this plainer data structure The most crucial part of the job is done bythe getList() method, which operates on a discussion node, getting a sequentiallist of descendant nodes by recursion getList() is what is known as a foreignmethod It does something the node object might have done itself The reason we don’tlet the node object do it is because we want to keep the presentation logic out of thediscussion node
Listing 13.12 shows the DiscussionView class The list it generates is an array ofarrays; so it’s not an object-oriented structure at all, but it’s a structure that’s simple touse in a template (You can use a simplified object-oriented structure instead if yourtemplate engine supports this.)
is easier to display
Listing 13.12 The DiscussionView class transforms a hierarchical threaded
dis-cussion into a linear data structure
Trang 16get-C To get the data from the database, we use a Data Mapper called DiscussionMapper.Although we haven’t introduced Data Mappers yet, to understand this example, youjust need to know that it’s a class that can be used for getting data from the database.
D The mapper’s find() method takes the discussion ID and retrieves the discussionfrom the database The discussion is an object-oriented tree structure composed ofdiscussion nodes
E Now we call the method that converts the Composite structure into a simple list inthe form of an array
F We remove the first element of the list It’s the root node representing the entire cussion, and we don’t want that to show up on the web page We just want the indi-vidual threads, which are the children of the root node
dis-G The getList() method returns the contents of the discussion as an array in theorder the posts will be listed on the web page This is where the recursion happens
H We have a $depth variable to keep track of the current level in the hierarchy Whenthe method is called initially (on the root node), $depth is set to -1 and thenincremented So the root node’s depth is 0 Then, when we call the method on the
1)
1!
Trang 17children of the root node, we pass $depth on and it gets incremented to 1 And so itkeeps increasing as we move recursively to deeper levels.
An inelegant but relatively flexible way to use this is to generate separate CSS classes for each level (level1, level2, and so on) Assuming a limited number of levels, they can
be separately styled in this manner:
table#AdminList tr.level2 td.threaded { padding-left: 2em; }
table#AdminList tr.level3 td.threaded { padding-left: 4em; }
table#AdminList tr.level4 td.threaded { padding-left: 6em; }
table#AdminList tr.level5 td.threaded { padding-left: 8em; }
I $array is an associative array representing a single node The asArray() methodconverts the node from an object, potentially with children, to a plain associative array
J $result is the list that will contain this node and all its descendants We build thelist starting with the current node
1) Each child generates a list of nodes, and we append the list to the result list
1! Since a plain array is relatively easy to use with any template engine, generating anarray from an object, as we’re doing here, may be reasonable However, if we can, itmight be better to use the object directly or via a decorator An object-oriented datastructure is more flexible and easier to modify
13.4.5 Preventing updates from the template
If we represent our data as objects, it’s convenient to be able to pass the object to thetemplate and use methods inside the template to display the data inside it But what
if this allows a template designer to modify the object and perhaps even store it in thedatabase? Now we have the same kind of security problem as with template languagesthat allow PHP code, although perhaps to a lesser degree In principle, a templateshould not be allowed to change anything It should only have access to read-onlydata, or to its own copies that are not used anywhere else
This is a case where the PHP 5 object model may be a hindrance rather than a help
In PHP 4, objects were copied by default, so a template would always get its own copies
of the objects So even if a template designer were to modify the object, it would notaffect anything outside the processing of the template
We can solve that problem by explicitly cloning the objects This can be built into
a template engine by decorating it or extending it with a subclass (I would normallyprefer a decorator to reduce the dependency on the template engine API, but we’reusing inheritance here to illustrate the possibility.)
class Template extends PHPTAL {
public function set($name,$data) {
if (is_object($data)) $data = clone $data;
parent::set($name,$data);
}
}
Trang 18But there is another, potentially worse problem: The object may have methods thataffect the outside world In particular, it might have methods to insert, update, ordelete itself in the database A clone would have the same power to do that as theoriginal object.
There are several ways we might handle this problem We might
• Use Data Mappers Data Mappers are specialized objects that handle databaseinteraction So a User object would not be able to insert itself into the database.Instead, we would have to use a UserMapper And there would be no way to gethold of the UserMapper from the template unless it was PHP-enabled
• Use a template engine-specific way to restrict access to methods Smarty allows you
to specify a list of allowed methods in Smarty’s register_object() method
• Decorate the object or copy it to a specialized View object containing the samedata but having fewer capabilities
Security considerations have been mentioned along the way in this chapter, but in thenext section, we’ll summarize and complete them
The most important issue is the danger of cross-site scripting (XSS) attacks (For anintroduction to this and other security-related concepts, see appendix B.) To preventthis, we need to escape all output The template engines described in this chapter arevery different in how they escape output Preferably, we want the template engine toescape output by default In other words, output escaping should be the easiestoption for the programmer and/or designer Template engines support this to differ-ent degrees and in different ways In this section, we’ll take a closer look at how itworks in PHPTAL, Smarty, and XSLT
13.5.1 PHPTAL
PHPTAL escapes all output variables by default This is excellent for security Butusing the structure keyword disables escaping for a variable:
<p tal:content="structure introduction">dummy intro</p>
Obviously, we should be careful when using structure If the variable containsdata that may come from the user, there is a risk In addition, you should make sureoutput is escaped with the correct character encoding The encoding should matchthe encoding set in the HTTP header PHPTAL’s default is UTF-8, which is often agood choice However, if you do need to use a different encoding, you can set it withthe constant PHPTAL_DEFAULT_ENCODING:
define('PHPTAL_DEFAULT_ENCODING', 'ISO-8859-1');
$tpl = new PHPTAL('abc.html');
Trang 19Although this is probably less relevant, it’s also possible to set the encoding for a gle template:
This can be achieved by using the $default_modifiers variable:
There is one exception: xsl:copy-of makes a deep copy of the current node inthe input XML file, including child nodes
As mentioned in the comments to listing 13.9, escaping variables from PHP may
be necessary mainly to avoid XML syntax errors The file to be transformed has to bevalid XML or the XML parser will complain If it contains arbitrary text, there is a highrisk that the text will contain some characters that will make it invalid
One of the central dogmas of modern web programming is the need to separateHTML markup from program code Although many believe this can be done effec-tively with plain PHP, others find it more appropriate to use a template engine All thetemplate engines meet roughly the same challenges, but they do so in syntacticallydifferent ways Some, such as Smarty, use a custom syntax exclusively XSLT, althoughnot strictly a template engine, is a specialized programming language that transforms
Trang 20an XML file containing the data to be displayed, adding markup to it PHPTAL usesXML attributes to specify dynamic content.
A powerful template engine typically has the ability to execute PHP code or otherpotentially advanced constructs This makes it all too easy to slip back into an exces-sively strong mixture of markup and program code Fortunately, there are additionaltechniques for handling the challenges—such as alternating row colors and date andtime formatting—that tend to lead you into that particular swamp
Templates pose particular challenges to security To guard against attacks, we need
to make sure we escape all output This is always possible, though easier with sometemplate engines than with others
Web presentation becomes even more demanding when the web page is composed
of many interacting components In the next chapter, we will look into what is oftencalled the Composite View pattern We will see how to gain layout and content flex-ibility both for the whole web page and its parts and how to integrate existing appli-cations into a Composite View
Trang 21C H A P T E R 1 4
Constructing complex
web pages
14.1 Combining templates (Composite View) 325
14.2 Implementing a straightforward composite view 326
14.3 Composite View examples 332
14.4 Summary 337
A complex web page is like a zoo There may be all sorts of different creatures, allwith different habits and requiring different care, cleaning, and feeding Some ofthem are in cages (typically the stuff that surrounds the main content, such as ban-ners, ads, menus, and various kinds of sidebars); some of them range freely on themain expanse of the page
Keeping all these coordinated is one of the great challenges of web programming
In addition, different species play together A menu may need to communicatewith a news list as well as with itself Making this work properly is actually a challengethat goes beyond the scope of this chapter, since that challenge involves user interac-tion Here, we will focus mostly on the display or View part of the job
In this chapter, we’ll first introduce and discuss the Composite View pattern Thenwe’ll show how to implement a simple, straightforward composite template usingSmarty or PHPTAL Finally, we’ll see how to solve a few more advanced challenges
Modern web pages are not just complex; they tend to grow increasingly complex But
we have some tools to help us
Trang 22Assembling a page is not really difficult with PHP include files You just use oneinclude file for each part of the page, and one file that includes all of them Most tem-plates have include capabilities as well There is no magic or rocket science involved.But careful thinking is needed to develop a structured approach that gets you the nec-essary flexibility and avoids inelegant hacks even when solving problems such as sep-arate print-friendly views of a page That’s what we’ll develop in this chapter.
14.1.1 Composite View: one or several design patterns?
The book Core J2EE Patterns (and its companion online pattern catalog) [Alur et al.]
has Composite View listed as a design pattern and demonstrates several differentstrategies for implementing it The Composite View itself is the idea of assembling aweb page from pluggable, reusable components The solutions to this problem arepresented as different strategies that are actually completely different solutions to thesame problem This may be confusing if you’re used to design patterns that give a rea-sonably specific solution to a problem
That need not trouble us too much, though The challenge is to achieve the kinds
of flexibility we need for developing complex layouts In PHP, this is typically achieved
by using the built-in features of PHP or template engines
14.1.2 Composite data and composite templates
The Composite View is one of the harder challenges in web programming One keyidea that is not widely recognized is this: assembling the template from componentsand assembling the data that goes into it (parts of the Model in Model-View-Con-troller terms) are two separate challenges You can have a monolithic class that doesthe whole job of creating the data for the template even if the template itself is assem-bled from several pieces And you can have a complex composite or collection of PHPcomponents that assemble and insert the data into a template, even if the template is
a single sheet of HTML with slots for dynamic information
The following sections will focus mostly on creating the composite template We’llfirst see how to do it in a typical, straightforward case
To design a strategy for assembling web pages, we need to know the requirements.How much and what flexibility do we need? The solution featured in the J2EE book[Alur et al.] is based on the idea of pluggable components and pluggable layout, anduses custom tags to achieve it
In this section, we’ll first define more specifically what we need to do Then we’llsee how it can be implemented with two template engines; first Smarty, then PHPTAL.We’ll also look at an additional, PHPTAL-specific way of doing it
Trang 2314.2.1 What we need to achieve
To get an idea of what it takes to implement a Composite View, let’s do a simple ple in plain PHP, starting with the simplest-possible implementation Figure 14.1shows the kind of layout we want
exam-We have four different components here: the banner, the menu, the main text, andthe sidebar containing the news list To implement this in “naive” PHP, we use plaininclude statements:
We should be able to replace the overall layout of the page with a different one Againstaying within plain, blunt PHP, all we need to do is make a separate file out of thepreceding code and include that from our main script:
Figure 14.1 Composite View-type page layout with several different components
Trang 24// Find out which $layout to use
//
include "$layout.php";
Wait a minute, you might say There’s no layout at all in the layout file! No HTMLtags Yes—and no The only layout that’s present is the presence and sequence of thecomponents We will assume that the rest is in CSS Each of the included files will be
an HTML<div> element with contents such as this:
<div id="banner">
<img src="hazycrazy2.png" width="519"/>
</div>
Each of these <div>s, and individual elements
within them, can be styled, positioned, even
hid-den, using CSS In fact, since CSS can be used
for positioning and hiding, you might even
question whether there is a need for the layout
file at all With CSS, you can position <div>s
accurately and (somewhat) freely regardless of
their sequence in the HTML markup And you
can hide them using display:none But the
hidden elements will still be present in the web page that is downloaded to thebrowser So they will still consume bandwidth, potentially making response timeslonger for the user
Figure 14.2 shows how we’ve named the parts of the page
14.2.2 Using Smarty
In our Composite View implementation, we want something that satisfies therequirements and leverages the tools we have available to make the solution as simple,easy, and maintainable as possible We can use Smarty to do something similar towhat we did with plain PHP in the previous section:
Trang 25b Using $current this way is not as dangerous as using it in a PHP include Still, ifthe variable can be altered by a user, there is a potential for retrieving a file from any-where in the file system.
There is one more thing we might like to do We need to handle the title of theHTML document as well Frequently, the main content area has a heading, for example:
be updated and not the other Having them both in one file would be better.What we can do is use Smarty’s capture feature to define a template section as
a variable that can be used somewhere else We would define the title and tent sections in the same file as follows:
Figure 14.3 We want the same text for the title and the main heading,
even though they are in separate parts of the page.
Trang 26<link rel="STYLESHEET" href="hazycrazy.css"
In PHPTAL, we also need to use macros in place of the plain includes This is a goodthing, since we can choose to have several macros per file, or just one
Using one file for the pluggable content and one for the other macros, our maintemplate file looks like this:
Trang 27Unlike many attributes used in PHPTAL (such as tal:content), macro accepts a string by default, not a variable So to access the file name in thecontent variable, we have to use ${content}.
metal:use-14.2.4 Using page macros with PHPTAL
The PHPTAL solution shown in the previous example is the one that follows the samestrategy we used for Smarty This strategy is useful since it’s generally applicable tomost, if not all, template engines In addition, PHPTAL allows us to be even more flex-ible by using what is known as a page macro The web page as a whole can be defined
as a macro, making it possible to handle the parts and the whole in one uniform ion
fash-Listing 14.1 is a page macro example Note first that this is not a template; it’s a
macro that defines what’s common between many pages If we forget that it’s a macroand try to use it as a template, we get no output
The general idea is that the page macro contains all of these elements:
• Static parts of the page such as the menu
• “Slots” that can be filled by the template that uses the macro
• Default content for the slots if the macro doesn’t fill them
No default content d