p.name like ‘%{@terms}%’ Your second XSQL page is very similar to the product details page.. Then you’ll handle the two product listing pages—the search results page and the product-
Trang 1name, and price while the xsql:include-owa action is used to return the XML doc-ument using the get_product_xml procedure used earlier
<?xml version=”1.0”?>
<prod-details connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-xsql href=”cat-nav.xsql”/>
<xsql:query rowset-element=”PRODUCT-SET”
row-element=”DETAILS”
product_id=”0”
bind-params=”product_id”>
SELECT id,name,price FROM product WHERE id=?
</xsql:query>
<xsql:include-owa product_id=”0” bind-params=”product_id” >
get_product_xml(?);
</xsql:include-owa>
</prod-details>
The last page for the public interface is the search results page It uses the query developed earlier and, like our other subordinate pages, includes the navigational ele-ment on the left
<?xml version=”1.0”?>
<prod-search connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:include-xsql href=”cat-nav.xsql”/>
<xsql:query rowset-element=”PRODUCT_SEARCH”
row-element=”PRODUCT”>
SELECT id, name, a.doc.extract(‘/product/summary/text()’).getStringVal() AS summary FROM product a
WHERE contains(doc,’{@search_terms}’)>0
ORDER BY contains(doc,’{@search_terms}’)
</xsql:query>
</prod-search>
You have three XSQL pages for the price editor interface The first displays the nec-essary information in response to a lookup:
<?xml version=”1.0”?>
<prod-search connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”>
<xsql:query rowset-element=”PRICE_EDITOR_SEARCH”
row-element=”PRICE_EDITOR_PRODUCT”>
SELECT p.name AS product_name,
pc.name AS product_cat_name, p.price
FROM product p, prod_cat_joiner pcj, product_category pc
WHERE p.id=pcj.product_id
AND
Trang 2p.name like ‘%{@terms}%’
</xsql:query>
</prod-search>
Your second XSQL page is very similar to the product details page The information for this page will be used to fill the values for the fields of a particular editor
<xsql:query connection=”momnpup”
xmlns:xsql=”urn:oracle-xsql”
rowset-element=”PRODUCT-SET”
row-element=”DETAILS”
product_id=”0”
bind-params=”product_id”>
SELECT id,name,price FROM product WHERE id=?
</xsql:query>
The last page to create is the page that handles the price change:
<price-editor xmlns:xsql=”urn:oracle-xsql” connection=”momnpup”>
<update>
<xsql:dml commit=”yes”
product_id=”0”
bind-params=”new_price product_id”>
UPDATE product
SET price=?
WHERE id=?
</xsql:dml>
</update>
<xsql:dml>COMMIT</xsql:dml>
<xsql:include-xsql href=”edit-prod-details.xsql”/>
</price-editor>
Now you have all of your XSQL pages created You can test them using URLs to make sure that you are getting the results you want back In the next section, the stylesheets are created to transform the raw data into something useful for your users
Writing the Stylesheets
Earlier in this chapter, you developed the mock Web site In the previous section, you got the XSQL setup to pull the data from the database Now you pull the two together using XSLT stylesheets The elements you’ll use were all covered earlier in this chapter What you’ll see here is how to fit the pieces together in a real application As with the preceding XSQL pages, you’ll see how to reuse and nest stylesheets You’ll also see techniques that are specific to XSQL
You’ll start at the top of the site and develop the home page Then you’ll handle the two product listing pages—the search results page and the product-by-category page This section also includes the development of the product category navigational menu The last page of the public interface is the product details page, and then the two
Trang 3stylesheets for the price editor will be developed Two points of functionality are cov-ered in later sections of this chapter: (1) the stateless paging of product result sets and (2) parameter passing In reality, you’ll probably want to solve those problems at the same time as the stylesheet problems that you’ll be solving here However, from a dis-cussion standpoint, it is far easier to cover those topics in their own sections
N OT E Before going any further, there is one fact of XSQL of which you should
be aware Regardless of how you set the rowset and row element names in your XSQL, they will be uppercase in your output In the preceding examples they were always set uppercase, so you won’t notice the difference Because XML is case sensitive, though, it’s important to be aware of this before you start writing stylesheet code If you specify lowercase rowset and row
elementnames in your XSQL and then write your stylesheets expecting those, your stylesheets won’t work The solution is simple—just uppercase the element names in your stylesheet expressions.
First on the hit list is the home page As with all of the XSQL pages, your first step is
to link to the stylesheet in your XSQL For home.xsql, you should add the following
as the second line:
<?xml-stylesheet type=”text/xsl” href=”home2.xsl”?>
Now it’s time to create the stylesheet itself The following code is the top of the doc-ument and the main template that is invoked for the top-level element, “home” There are other templates to add, so you don’t have a closing stylesheet tag yet
<?xml version = ‘1.0’?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:include href=”banner.xsl”/>
<xsl:template match=”/home”>
<html>
<head>
<link rel=”stylesheet” type=”text/css” name=”catalogue”
href=”catalogue.css”>
</link>
</head>
<body>
<table width=”800” border=”0”>
<tr><td colspan=”2” height=”100”>
<! banner table >
<xsl:call-template name=”banner”/>
</td></tr>
<tr>
<td width=”600”>
<! product category table >
<xsl:apply-templates select=”PRODUCT_CATEGORIES”/>
</td>
Trang 4<! promotion table >
<xsl:apply-templates select=”PROMOS”/>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
As promised, there are no problems integrating CSS with XSLT We’ll use references
to CSS styles throughout the code examples Working down through the example, the next thing to notice is the xsl:include of banner.xsl The banner.xsl stylesheet contains a single template-named banner It is called with an xsl:call-template element Notice that no parameters are passed to it Here we are using XSLT for a sim-ple purpose—HTML reuse By separating the banner into its own file, it can be called from all of our files If we want to change it, we can change it once, and it will be changed throughout the site The fact that it doesn’t actually interpolate any of the inputted XML data doesn’t actually matter Here is the banner.xsl stylesheet:
<?xml version = ‘1.0’?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:template name=”banner”>
<table width=”100%” height=”100” class=”banner-style” border=”0”>
<tr>
<td colspan=”2”>
<span class=”banner-title”>
Mom N’ Pup
</span>
</td>
</tr>
<tr>
<td align=”left” valign=”top”>
<i>
<span class=”banner-subtitle”>
Catalogue
</span>
</i>
</td>
<td align=”right” valign=”top”>
<form action=”prod-search.xsql” method=”post”>
<span class=”banner-subtitle”>
Search: <input name=”search_terms” size=”30”></input>
<input type=”submit” value=”go!”></input>
</span>
</form>
</td>
</tr>
</table>
</xsl:template>
Trang 5The banner code itself isn’t particularly interesting There aren’t even any XSLT elements besides xsl:stylesheet and xsl:template It could, of course, contain any XSLT elements However, because xsl:call-template is used to invoke the template, you don’t know what the context node will be, and thus your XPath expres-sions should be absolute As discussed in Chapter 13, you can pass parameters to a template that is invoked with xsl:call-template and have functionality similar to
a subroutine
The one thing to note in the banner.xsl is the search field It links to the prod -search.xsqlpage using a post query You’ll develop that stylesheet in another cou-ple of pages For now, it’s time to round out the home.xsl stylesheet that we started earlier Looking at the top-level template for home.xsql, you can see that two other templates are invoked with apply-templates These templates in turn invoke their own templates Let’s start with the “PROMOS” template and its associated template:
<xsl:template match=”PROMOS”>
<table border=”0”>
<xsl:apply-templates select=”PROMO”/>
</table>
</xsl:template>
<xsl:template match=”PROMO”>
<tr>
<td width=”200” height=”100”>
<a>
<xsl:attribute
name=”href”>prod-details.xsql?product_id=<xsl:value-of select=”PRODUCT_ID”/></xsl:attribute>
<img width=”200” height=”100”>
<xsl:attribute name=”src”>
<xsl:value-of select=”URL”/>
</xsl:attribute>
</img>
</a>
</td>
</tr>
</xsl:template>
Looking back at promo.xsql, you’ll see that it pulls two pieces of data on the promo: (1) the product ID and (2) the URL for the promo image The image is assumed
to be 200 x 100 A link is set around the image so that when you click on the image you are taken to the product details page
This template has the first of many uses of the xsl:attribute element to set the value of an HTML attribute You can’t use an XSLT element inside of an XML element, like <a>, so you have to set the attribute separately You may notice that the xsl: attributecode isn’t nicely tabbed like the rest of the example This is purposeful; If you put the xsl:value-of element on its own line, then there will be white space inside of your attribute value You should always keep all of an xsl:attribute element on one line
The “PRODUCT CATEGORIES” template is the most complex in our entire applica-tion There are several challenges First, the categories are separated into columns with
Trang 6the first categories appearing in the left-hand column and the later categories appear-ing in the right-hand column This is more complex than listappear-ing the columns in a left-to-right ordering To solve this, we have two separate tables nested into a higher-level table consisting of only two cells The xsl:apply-templates is called first on all categories that are in the first half of the result set, and then a separate xsl:apply -templatesis called on all categories in the second half of the result set If there is an odd number of categories, the right-hand column will have the extra
<xsl:template match=”PRODUCT_CATEGORIES”>
<table border=”0”>
<tr>
<td valign=”top”>
<! left hand table >
<table valign=”top” width=”300” border=”0”>
<xsl:apply-templates select=”CATEGORY[position() <= (last() div
2) ]”/>
</table>
</td>
<td valign=”top”>
<! right hand table >
<table valign=”top” width=”300” border=”0”>
<xsl:apply-templates select=”CATEGORY[position()>(last() div 2) ]”>
<xsl:with-param name=”align-val”>right</xsl:with-param>
</xsl:apply-templates>
</table>
</td>
</tr>
</table>
</xsl:template>
Notice that a parameter is passed for the right-hand categories This is because the mockup requires that the text in the left-hand column be left-adjusted, and the text in the right-hand column be right-adjusted You could develop two templates—one for each column—but most all of the code would be redundant Instead, you simply pass
a parameter and set the “align” attribute based on the parameter Because the default value of the parameter is “left”, it isn’t necessary to pass a parameter for the preceding left-hand apply-templates Here is the “CATEGORY” template that is invoked
<xsl:template match=”CATEGORY”>
<xsl:param name=”align-val”>left</xsl:param>
<tr>
<td>
<xsl:attribute name=”align”><xsl:value-of
select=”$align-val”/></xsl:attribute>
<span class=”category”>
<a>
<xsl:attribute
name=”href”>prod-cat.xsql?category_id=<xsl:value-of select=”ID”/></xsl:attribute>
Trang 7<b><xsl:value-of select=”NAME”/> </b>
<! product description >
<xsl:value-of select=”DESCRIPTION”/>.
</a>
<i><xsl:apply-templates select=”PRODUCTS”/></i>
<a>
<xsl:attribute name=”href”>prod-cat.xsql?category_id=<xsl:value-of select=”ID”/></xsl:attribute>
.
</a>
</span>
</td>
</tr>
</xsl:template>
For this template you pull the text values for the name and description elements for the category and link them to the product category page You also have to list the first products in the category The template for this follows It solves two problems First, only the first three elements are pulled while the rest are ignored Second, commas are only inserted between elements 1 and 2 and elements 2 and 3 This is a common prob-lem in XSLT and is easily solved with an xsl:if eprob-lement along with the position and last functions The individual product names are linked to the product pages
<xsl:template match=”PRODUCTS”>
<xsl:for-each select=”PRODUCTS_ROW[position() <= 3]”>
<a>
<xsl:attribute name=”href”>prod-details.xsql?product_id=<xsl:value-of select=”./ID”/></xsl:attribute>
<xsl:value-of select=”./NAME”/>
<xsl:if test=”position() < last()”>, </xsl:if>
</a>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
The last tag is the close of our stylesheet for the home.xsql You’ll now have a home page that looks like Figure 14.1 that appeared earlier in this chapter, but now all
of the data is database driven What’s really neat is that all of the links and search func-tionality work You still get back the ugly XML, but you are getting back actual data from the database Now, it’s time to make the rest of the application pretty
The search results page and the product category page present essentially the same set of challenges We might as well start with the product category page On the left of the page is the category directory As with the banner template, the template for the category directory will be separated into its own file The main stylesheet along with the stylesheet header is as follows:
Trang 8<?xml version = ‘1.0’?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:include href=”banner.xsl”/>
<xsl:include href=”cat-nav.xsl”/>
<xsl:template match=”/prod-cat”>
<html>
<head>
<link rel=”stylesheet” type=”text/css” name=”catalogue”
href=”catalogue.css”>
</link>
</head>
<body>
<table width=”800” border=”0”>
<tr><td colspan=”2” height=”100”>
<! banner table >
<xsl:call-template name=”banner”/>
</td></tr>
<tr>
<td width=”200”>
<! product category list >
<xsl:apply-templates select=”product_categories”/>
</td>
<td valign=”top” width=”600”>
<table>
<tr><td>
<a href=”home.xsql”>Home</a>:
<xsl:value-of select=”//CATEGORY_NAME”/>
</td></tr>
<tr><td>
<! search results >
<xsl:apply-templates select=”PRODUCTS”/>
</td></tr>
</table>
</td>
</tr>
</table>
</body>
</html>
</xsl:template>
First, notice that the top-level template references a different element than home.xsl The top-level home.xsql page outputs “home” as the top-level element, whereas the top-level element here is “prod-cat” By having a different root element for each XSQL page, you are able to build distinct templates for the different types of data If you ever need to intermingle the templates, it will be easier to do so
Trang 9As with the home page, the banner template is invoked to create the banner at the top
of the page The product-categories template is in the cat-nav.xsl stylesheet covered later It handles the directory listing on the left-hand side of the page
From there, the template precedes simply The category name is pulled from the XML, and then the template for the set of products is invoked For the category name
we use the // to mean “child-or-descendant” Because this axis appears at the begin-ning of the expression, it means child or descendent of root This syntax is a little dan-gerous because it assumes that there is only one element of that name in the entire XML document In our case we know for certain that there is only one CATEGORY_NAME, so it’s okay The alternative would be to spell out multiple layers in order to access this one data point XSQL always expects there to be more than one row, so the XML is always structured that way However, if there really is only one row, it’s just another layer of elements that needs to be navigated Used with caution, the child-or-descendant axis can make your code a little more readable when you are only pulling one row of data The products template and its child template, called”PRODUCT”, follow These are fairly straightforward For each product in the result set, the product name and sum-mary are listed The product names are linked to the respective product details pages
As this is the end of the stylesheet, the end tag is also included
<xsl:template match=”PRODUCTS”>
<table class=”search-results” width=”100%”>
<th><span class=”search-results”>Product</span></th><th>Summary</th>
<xsl:apply-templates select=”PRODUCT”/>
</table>
</xsl:template>
<xsl:template match=”PRODUCT”>
<tr>
<td>
<a>
<xsl:attribute
name=”href”>prod-details.xsql?product_id=<xsl:value-of select=”PRODUCT_ID”/></xsl:attribute>
<xsl:value-of select=”PRODUCT_NAME”/>
</a>
</td>
<td><xsl:value-of select=”SUMMARY”/></td>
</tr>
</xsl:template>
</xsl:stylesheet>
Before moving onto the product search page, we need to back up and do the cat- egory navigation page It is a straightforward stylesheet that matches up with the cat-nav.xsqlpage The cat-nav.xsql page furnishes the XML as a top-level element, and the cat-nav.xsl page takes that XML and formats into a list of linked category names
<?xml version = ‘1.0’?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
Trang 10<table valign=”top” width=”100%” height=”100%” class=”cat-directory”
border=”0”>
<tr><td><b>Categories</b></td></tr>
<xsl:apply-templates select=”category”/>
</table>
</xsl:template>
<xsl:template match=”category”>
<tr>
<td>
<a>
<xsl:attribute name=”href”>prod-cat.xsql?category_id=<xsl:value-of
select=”ID”/></xsl:attribute>
<xsl:value-of select=”NAME”/>
</a>
</td>
</tr>
<tr>
<td height=”10%”> </td>
</tr>
</xsl:template>
</xsl:stylesheet>
On to the search results page! The search results page is very similar to the product category page and its full display follows The one difference is that the linked category name for a product is included in the results The primary category for the product is used
<?xml version = ‘1.0’?>
<xsl:stylesheet xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
version=”1.0”>
<xsl:include href=”banner.xsl”/>
<xsl:include href=”cat-nav.xsl”/>
<xsl:template match=”/prod-search”>
<html>
<head>
<link rel=”stylesheet” type=”text/css” name=”catalogue”
href=”catalogue.css”>
</link>
</head>
<body>
<table width=”800” border=”0”>
<tr><td colspan=”2” height=”100”>
<! banner table >
<xsl:call-template name=”banner”/>
</td></tr>
<tr>
<td width=”200”>
<! product category list >
<xsl:apply-templates select=”product_categories”/>
</td>