Now you know enough to have some real fun, so we'll do a real Embperl project ”a product filtering system. 10.8.1 The ProblemWe will create two web pages: The first enables the user to filter a list of products, narrowing the selected items based on three criteria: the manufacturer's city, the manufacturer's name , or the product category (the user can select any combination of these three categories); the second page provides details for a selected product. For a sneak preview, see http://localhost/embperlproject/productfilter/ or www.opensourcewebbook.com/embperlproject/productfilter/. These Embperl pages are built from a MySQL database, so the first step is to create the tables and populate them with data. We will have two tables: a table of manufacturers and a table of products. Each manufacturer has a unique manufacturer ID, and each product relates to a manufacturer through this ID. The manufacturers table (named manufacturers , strangely enough) has the following fields:
The products table (named products , strangely enough) has the following fields:
Now we need to log in to MySQL to create the tables and insert some data. One way to accomplish this is for us to show you the commands and have you type them in. If we were mean, we would do this. However, there is another approach: Create the tables and insert the data using a text file that contains the table definition and the MySQL insert commands, and use a single, simple command to build the tables. We're not mean, so we have created this file for you. You can find it at /var/www/misc/product_database.sql or online at http://localhost/embperl/product_database.sql or www.opensourcewebbook.com/embperl/product_database.sql. To magically build this, first log in to MySQL and create the database: $ mysql -uroot -p Enter password: Welcome to the MySQL monitor. Commands end with ; or \g. Your MySQL connection id is 3 to server version: 3.23.36 Type help; or \h for help. Type \c to clear the buffer mysql> CREATE DATABASE product_database; Query OK, 1 row affected (0.00 sec) mysql> USE mysql; Reading table information for completion of table and column names You can turn off this feature to get a quicker startup with -A Database changed mysql> GRANT SELECT,INSERT,UPDATE,DELETE -> ON product_database.* -> TO apache@localhost; Query OK, 0 rows affected (0.00 sec) Execute this shell command from directory that contains this text file: $ mysql -uapache -p product_database < product_database.sql Enter password: Like magic, the database is created and populated . You're welcome! 10.8.2 Creating a Consistent Look and FeelBefore we get into the nitty-gritty of the project, let's talk in general about some options we can use to create a web site with a consistent look and feel (such as www.opensourcewebbook.com/). There are several ways to create such a web site with a consistent look and feel to all the web pages.
Method 1: Hard-Code HTML FilesAll the pages can be hard-coded with the same HTML. Although this seems like the simplest thing to do, it isn't a great idea ”if any content changes in the common parts , such as adding a link or changing the placement of a logo, every page has to be modified. Yuck. Good programmers are lazy, so a little work up front can save a lot of work later. Method 2: Use WML to Create HTML FilesUsing WML, as in Chapter 6, is much better than hard-coding HTML files. Creating a template for a look and feel is possible, so when (not if) changes to the look and feel occur, the changes need only be made once and will apply to all the HTML files. This is fine for static pages. WML can be used with Embperl because WML has the <protect> tag, which makes WML ignore all the content within. Thefore, the WML files can be used to generate HTML, and that HTML can also contain Embperl code. We don't discuss this here because, in practice, wrapping <protect> ... </protect> around all the Embperl code can cause us to have dozens of protected blocks, and that is not pleasant. We don't go into this except to mention it here, but keep in mind that you can use WML and Embperl to create a consistent look and feel. TMTOWTDI in all things LAMP. Method 3: Use CGIWe can certainly use CGI to create dynamic web sites; we did so in Chapter 7. Just take the HTML created by the web designers, add a bunch of print() functions, add our code to do the dynamic stuff (such as reading from a database), and we can solve the problem. But this requires us to transform the HTML files into Perl programs. Sometimes this is more work than we want ”remember, good programmers are lazy. Method 4: Use mod_perlmod_perl, discussed in Chapter 8, is great for many uses, especially if we want to access Apache directly or if we want to speed up the CGIs. But, as in CGI, creating mod_perl pages means we convert the HTML into a program. And as we mentioned earlier, most of the features of mod_perl are available to our Embperl programs (such as speed of execution because of caching and access to Apache internals), so why convert? Method 5: Use Embedded Programs to Build HTMLWe can create a couple of functions to generate the HTML for the top and the bottom of the HTML. Then, in the HTML files, we can have Embperl code that calls these functions to generate the HTML for the look and feel. As you might have guessed, because this is the Embperl chapter, this is the method we talk about here. To illustrate , we'll create a file to hold functions for the top and bottom of the HTML. We'll put the code in a file not within the document tree ”put it in /var/www/lib/embperl_template.pl . Hey, did you notice this is outside the web document tree /var/www/html ? That is good security because the program is not viewable by someone visiting the web site, and therefore we don't show them the internals of how we are building the web pages. The less information we give crackers, the better. First create this directory, make it accessible by user apache , and then create the file within it that is readable by apache : # mkdir /var/www/lib # chmod a+rx /var/www/lib # cd /var/www/lib # vi embperl_template.pl # chmod a+r embperl_template.pl These are the contents of embperl_template.pl : # file: /var/www/lib/embperl_template.pl ## ## the subroutine to return the HTML for the top ## of the page ## sub top { return <<EOHTML; <html> <head> <title>Embperl Project Take 1</title> </head> <body bgcolor="#ffffff"> <h1>A First Stab at Our Embperl Project</h1> EOHTML } ## ## the subroutine to return the HTML for the ## bottom of the page ## sub bottom { return <<EOHTML; </body> </html> EOHTML } 1; # don't forget this line! This file simply defines two functions: top() and bottom() . The top() function returns the HTML for the top of the HTML, and the bottom() function returns the bottom of the HTML. Two things to note: First, there is no shebang ( #!/usr/bin/perl ) in this file because it is not an executable file but a file that defines a couple of functions that are used by other programs; second, the file must end with 1; . This is the same syntactical Perl requirement seen in Chapter 8; the file must evaluate to true in the end, or the compilation fails, and the easiest way to guarantee a true value is by ending the program with 1; . Now create a file that will source these two functions. Go to the directory with all of the Embperl code, /var/www/html/embperl , and create the file take1.html : [- require /var/www/lib/embperl_template.pl; $escmode = 0; -] [+ top() +] This is a test of the Embperl Project Take 1 [+ bottom() +] Load one of these URLs: http://localhost/embperl/take1.html or www.opensourcewebbook.com/embperl/take1.html. You should see Figure 10.7. Figure 10.7. Embperl project take 1
At the top of take1.html is a [- ... -] command; the Perl code within will be executed. The first line of this Perl code is a require . This statement will go out, find the file in question (the embperl_template.pl file containing the top() and bottom() function definitions), source that file, and compile the functions. Then, we see $escmode = 0; . This is an important statement and deserves some discussion. When Embperl generates output, as with the top() function, Embperl automatically HTML- and URL- escapes it. In other words, if the Embperl code generates <hr> , it is escaped to be <hr> , which is displayed in the browser as the characters <hr> , not as the horizontal rule we wanted. There are several solutions to this problem, including escaping the characters we don't want to be HTML-escaped or URL-escaped with the backslash, as in \<hr\> . You can imagine that this could become a real drag if a whole mess of HTML is generated. The better solution is to assign $escmode the value , which turns off this HTML- and URL-escape behavior. If this is turned off, when <hr> is generated, it indeed becomes a horizontal rule. But there is a drawback to using require in this manner. Before we describe the problem click the Reload button on the browser a couple of times to reload this page (so that some of the many httpd processes will read in and compile the top() and bottom() functions). Modify the top() function to add more HTML. Change the definition of top() to: ## ## the subroutine to return the HTML for the top ## of the page ## sub top { return <<EOHTML; <html> <head> <title>Embperl Project Take 1</title> </head> <body bgcolor="#ffffff"> <h1>A First Stab at Our Embperl Project</h1> Here is some text followed by a horizontal rule. <hr> EOHTML } We simply added some text followed by a <hr> tag. Reload take1.html , then reload several more times. Chances are that sometimes you will see the new HTML, and sometimes the old. Why? When new httpd processes are started, they compile the new definition. But the existing httpd processes do not recompile them, because Embperl has cached the compiled code. Embperl recompiles the Perl byte code if the Embperl file ( take1.html ) has changed, but in our case it hasn't ” only the template file embperl_template.pl has changed. Moreover, once the Apache child process has compiled embperl_template.pl , it won't recompile it even if its contents change ”the only file that is recompiled is the page that is requested (in our example, take1.html ), not the pages that they require. Remember, because this speeds up execution time, this is one of the advantages of Embperl! How do we force the existing versions of httpd to recompile the changed functions? Reload Apache. This is not ideal, because one has to be root to reload, which makes it a pain during code development. To prove this actually solves the problem, reload Apache (as root , of course): # /etc/init.d/httpd graceful Now, reload the page as many times as you like, and you will see the new content on all the pages. All the versions of httpd have the new definition of top() . Method 6: Use HTML::EmbperlObjectThere's a more powerful way to build a web site with Embperl: HTML::EmbperlObject , aka EmbperlObject. This Perl module allows the creation of templates for the entire web site or for portions thereof, and simple modifications to the HTML in the templates take effect immediately ”there is no reason to restart the server. It works like this: When an .html file is requested, a base file (call it base.html , configured in httpd.conf ) is searched for in the directory that contains the file requested. This base.html contains HTML for the look and feel of the web site ”the top, left rail, and bottom HTML. If base.html is not found in that directory, EmbperlObject looks in directories up the tree until it finds one named base.html . If EmbperlObject searches up to the directory root ( /var/www/html , in these examples) and does not find a base.html , the user sees a "Not Found" error. We start with a simple example to illustrate the concepts and then move on to implement a project. First we must create a new directory because the examples and project must be told to use the EmbperlObject module. Create a new directory: # mkdir /var/www/html/embperlproject # chmod a+rx /var/www/html/embperlproject Next, reconfigure Apache so it knows about the new part of the web site. As root , add the following to the bottom of Apache's configuration file: <Directory /var/www/html/embperlproject> PerlSetEnv EMBPERL_OBJECT_BASE base.html <Files *.html> SetHandler perl-script PerlHandler HTML::EmbperlObject Options ExecCGI </Files> </Directory> This directive tells Apache that under the directory /var/www/html/embperlproject , the EmbperlObject base file, or template file, is named base.html . Then, for all files that end in .html , process them with the handler HTML::EmbperlObject . As mentioned before, this directory is configured this way because Apache should serve up .txt , etc., from this directory as normal files. See the docs ( perldoc HTML::Embperl ) for all the different ways to configure this directory. When this is done, load the altered Apache configuration file: # /etc/init.d/httpd graceful A Simple ExampleThe base file is named base.html . You can create one in /var/www/html/embperlproject with this content: <html> <head> <title>A Simple EmbperlObject Example</title> </head <body bgcolor="#ffffff"> <h1>This is a simple EmbperlObject Example</h1> This text, and the following horizontal rule, is in base.html. <hr> [# the following will include the contents of the #] [# HTML file requested - `* means the requested file #] [- Execute (*)-] <hr> The above horizontal rule, and this text, is in base.html. </body </html> The line [- Execute ( * )-] is important here ”it means, "Include the content of the file that the user has requested (such as index.html )." So, in essence, the contents of base.html are wrapped around the contents of the requested file. The requested file is the last part of the URL. The first example of this, and the file that should be created, is (as usual) index.html , the file that is delivered if no specific .html file is requested. It is in the directory /var/www/html/embperlproject and has the following content: <i>This text, and the following message, is in index.html.</i> <br> <i><b>Hello, world!</b></i> <br> Simple enough. The desired result would be to see the previous HTML in the middle of the template base.html . Now give it a try by loading either of these URLs: http://localhost/embperlproject/ or www.opensourcewebbook.com/embperlproject/. You should see something like Figure 10.8. Figure 10.8. EmbperlObject simple example
The beauty of this is that if either base.html or index.html is changed, the effect is seen immediately without the need to restart Apache. Give it a try ”alter the content of base.html , see the change, and change it back to the content shown previously. Create another file in /var/www/html/embperlproject named cool.html : <h4>Why Open Source is Cool (in the file cool.html)</h4> <ul> <li>It is open, so you have all the code available - reading the code is a good way to spend a rainy day</li> <li>The programs are free</li> <li>The people involved in Open Source are cool, smart, and all around "good people"</li> <li>It is a movement bigger than any individual and more than the sum of its parts</li> </ul> To see this (important) content wrapped within the template, request cool.html with either http://localhost/embperlproject/cool.html or www.opensourcewebbook.com/embperlproject/cool.html. The content will show up within the template as in Figure 10.9. Figure 10.9. EmbperlObject simple example ” cool.html
This template is applied to subdirectories under the directory that includes base.html . As an example, make a new directory: $ mkdir /var/www/html/embperlproject/sub1 $ chmod a+rx /var/www/html/embperlproject/sub1 Within this new directory, create a new file named index.html with this content: This is <i>index.html</i> within the subdirectory <i>sub1</i>. Requesting this file will use the template <i>base.html</i> located in the parent directory <i>/var/www/html/embperlproject</i>, since there is no <i>base.html</i> in this directory. Look at this new file by asking for one of these URLs: http://localhost/embperlproject/sub1/ or www.opensourcewebbook.com/embperlproject/sub1/. The template defined in base.html in the parent directory is wrapped around the content of the new index.html in subdirectory sub1 . It looks like Figure 10.10. Figure 10.10. Using base.html in the parent directory
You can redefine the template for a specific subdirectory ”you don't have to use the same template for all the subdirectories. Create another subdirectory under /var/www/html/embperlproject : $ mkdir /var/www/html/embperlproject/sub2 $ chmod a+rx /var/www/html/embperlproject/sub2 Now define a new template within the file base.html . Make sure that this new file is created within the new subdirectory ”don't overwrite base.html in the parent directory. <html> <head> <title>sub2 Template</title> </head> <body bgcolor="#ffffff"> <h1>sub2 Template</h1> This is the template for all files within <i>/embperlproject/sub2</i>. <hr> [- Execute (*)-] <hr> Back to the template. Hey, check it out, we are including a footer file below! Perhaps it contains HTML that will be the bottom of the page? [- Execute (bottom.html)-] There's something interesting at the end of this example: [- Execute ( bottom.html )-] . One might surmise that this will source the file bottom.html located in the same directory, and that is exactly what it does. EmbperlObject files can include other EmbperlObject files. One can have a file for the top of the page, another for the navigation for the page, another for the bottom of the page, and so on. The file bottom.html is located in the same directory: <hr> We are at the bottom of the page. <br> For comments, please email <a href="mailto:webmaster@opensourcewebbook.com">the webmaster</a>. </body> </html> The contents of index.html in the subdirectory sub2 is simply this: <i>The contents of index.html.</i> To see the result of these three files, load either one of these URLs: http://localhost/embperlproject/sub2/ or www.opensourcewebbook.com/embperlproject/sub2/. You should see Figure 10.11. Figure 10.11. Using base.html in the same directory, plus a footer
Now, one more thing is needed for this example to be really useful: the ability to pass information into the files in question so that things can change dynamically, such as the title. In the base file, we can define a method; then, in other files, the same function can be redefined. The function names must be the same in all the redefinitions. The base file uses the latest function definition. To illustrate this, create a third subdirectory, named sub3 : $ mkdir /var/www/html/embperlproject/sub3 $ chmod a+rx /var/www/html/embperlproject/sub3 Create a very simple base.html including only the barest of HTML ”note the value of the title within the <title> tags: [! # remember that code in these types # of commands is executed only when # the file is first requested or after # the file is modified sub title { return The Default Title; } !] [- # now, each time the page is requested, # get the first argument passed in (it # is the request object) # we will then be able to call the # title() function above using our # request object $req = shift; -] <html> <head> [# the code $req->title() below executes the title() #] [# function defined in the file requested, or this #] [# file by default #] <title>[+$req->title()+]</title> </head> <body bgcolor="#ffffff"> [- Execute (*)-] </body> </html> The content of index.html is simply this: Hello, world! from <i>sub3</i>. This file does not redefine the title() function, so it uses the default definition found in base.html . Load this page in http://localhost/embperlproject/sub3/ or www.opensourcewebbook.com/embperlproject/sub3/. The file index.html uses the default title as shown in Figure 10.12. Figure 10.12. Using the default title
Now create a file that redefines the title() function. Call it newtitle. html , located in sub3 : [! # we redefine title(), so that base.html # will use this definition when this page # is requested sub title { return The title for newtitle.html; } !] This is the content of <i>newtitle.html</i>. Have a look at http://localhost/embperlproject/sub3/newtitle.html or www.opensourcewebbook.com/embperlproject/sub3/newtitle.html. The new title returned from title() , defined in newtitle.html , is shown in Figure 10.13. Figure 10.13. Using the title redefined in newtitle.html
Now all the pieces needed to do some serious web development are in place. You now can create templates that can be given specific information for the page requested, and the server doesn't have to be restarted every time something changes. 10.8.3 Product FilterNow you have the tools to code the product filter. Recall that we created a database of manufacturer and product information. We will now create two Embperl pages: /embperlproject/productfilter/index.html , to filter the list of products, and /embperlproject/productfilter/productdetail/index. html , to display the details of a specific product. A base.html file would be helpful to define the web site look and feel, so look at the file /var/www/html/embperlproject/productfilter/base.html , also found by clicking http://localhost/embperlproject/productfilter/base.html.txt or www.opensourcewebbook.com/embperlproject/productfilter/base.html.txt. We don't cover all of this file ”it is a bit too big ”but you can find its contents at www.opensourcewebbook.com. First, see the [! ... !] command: [! # the default title sub title { return Product Filter Embperl Style; } # the default bread crumb string sub bread_crumb { return productfilter; } !] This defines the functions to be used for the default title placed between the <title> ... </title> tags and the bread crumb text, which is processed in the following block of code: [- # get the request object $req = shift; # we are building a string with HTML in it, so turn off # HTML- and URI-escaping $escmode = 0; # get the bread crumb string and split it on the colon @crumbs = split /:/, $req->bread_crumb(); # build the bread crumb string - grab each pair of URI/text, # build the link $bread_crumb text = ; while (@crumbs > 1) { $href = shift(@crumbs); $text = shift(@crumbs); $bread_crumb_text .= "\<a href=\"$href\">\ <font color= \"#999966\">\<b>$text\ <b>\</font>\</a> "; } # add the last text in the bread crumb string $bread_crumb_text .= $crumbs[0]; -] First, the request object is gotten and assigned to $req . Then $escmode is set to 0 to turn off HTML- and URI-escaping because we are going to build some HTML to be included in the output. Then the bread crumb string is generated by calling the function bread_crumb() defined either in this file (the default) or a file that uses this base (which will override the default). The elements of the string are then processed. For each URI/text pair, the href and text are shifted out of @crumbs , and an HTML link is created and concatenated to $bread_crumb_text . When all the URI/text pairs are processed, the last text is concatenated onto $bread_crumb_text . Later we see the inclusion of the title defined in the title() function: <title>Open Source Web Book - [+$req->title()+]</title> Still later, we see the inclusion of the bread crumb string: - [+$bread_crumb_text+] Then, buried in all the HTML that builds the links on the left rail and other fun things, the statement that includes the HTML file uses this template: [- Execute (*)-] Now we come to the page that builds the filter form. It is in the file /var/www/html/embperlproject/productfilter/index.html at either http://localhost/embperlproject/productfilter/index.html.txt or at www.opensourcewebbook.com/embperlproject/productfilter/index.html.txt. The beginning [- ... -] command works pretty hard. use DBI; # connect to the database $dbh = DBI->connect(DBI:mysql:product_database, apache,LampIsCool); A connection is made to the database. Then we see the following code: # grab the posted data $posted_city = $fdat{city} ; $posted_manufacturer = $fdat{manufacturer} ; $posted_category = $fdat{category} ; # make sure that the posted data contains the data we want # (no nasty characters) and the length we expect - if the # data looks bad, set variable to empty string unless ($posted_city =~ / ^ [\w \.]+$/) { $posted_city = ; } if (length($posted_city) > 60) { $posted city = ; } This code grabs the posted data and does some sanity checks on its contents (we show only the sanity checking of $posted_city ; the other variables are similar). # $sql_cond will contain the conditions that we will use for # the SQL query - if we received any posted data, then we need # to append to $sql_cond the appropriate SQL text and we will # add the value of the variable to the array of execute() # arguments $sql_cond = ; @execute_args = (); if ($posted_city or $posted_manufacturer or $posted_category) { if ($posted_city) { $sql_cond .= AND manufacturers.city = ?; push @execute_args, $posted_city; } if ($posted_manufacturer) { $sql_cond .= AND manufacturers.name = ?; push @execute_args, $posted_manufacturer; } if ($posted_category) { $sql_cond .= AND products.category = ?; push @execute_args, $posted_category; } } The preceding code builds a string named $sql_cond , the text of which is included in the SQL query, and @execute_args , an array of values that is plugged into the " ? " in the query. <select name="city"> [# for each of the posted variables, generate the select #] [# box HTML - this will include an SQL query to get the #] [# data out of the database #] <option value="">Select One</option> <option value="">------</option> [- $sth = $dbh->prepare(SELECT DISTINCT city FROM manufacturers ORDER BY city); $sth->execute(); -] [$ while (($city) = $sth->fetchrow()) $] [$ if ($posted_city eq $city) $] <option value="[+ $city +]" selected>[+ $city +]</option> [$ else $] <option>[+ $city +]</option> [$ endif $] [$ endwhile $] </select> The preceding code builds the city selection button (there is similar code to build the button to select the manufacturer and the category). Then the database is queried for all the cities, and for each city, an option is added, marking the posted city selected, if appropriate. Notice that the Select One option and the ----- option both have the value of empty string ( "" ). This ensures that if the user selects one of these, the value will be empty string, so that category will not be used in the filter. [# now build the table - this query will include any conditions #] [# that we have based on the posted data from the filter #] [# options - we will then loop through the results, building #] [# the rows of the table #] [- $sth = $dbh->prepare(SELECT products.prod_id,products.name, products.category,products.price FROM manufacturers, products WHERE manufacturers.man id = products.man_id . $sql_cond . ORDER BY products.category, products.name); $sth->execute(@execute_args); $i=0; -] The preceding code queries the database for all products that match the filter criteria, including the selected city, manufacturer, and category, that were set in $sql_cond and @execute_args . [$ while (($prod_id,$name,$category,$price) = $sth->fetchrow()) $] [- if ($i%2==0){ $bgcolor = "#ffffff"; } else { $bgcolor = "#cccccc"; } $i++; -] <tr bgcolor="[+ $bgcolor +]"> <td> <a href="/embperlproject/productfilter/productdetail/ ?prod_id=[+ $prod_id +]"><font color="#999966"> <b>[+ $name +]</b></font></a></td> <td>[+ $category +]</td> <td>$[+ $price +]</td> </tr> [$ endwhile $] In the preceding code, the background color alternates between white and gray on each loop, making the table more readable. A link is created for the product detail page (wrapped for readability), passing the product ID into its %fdat , and then the data is displayed. Check page by clicking http://localhost/embperlproject/productfilter/ or www.opensourcewebbook.com/embperlproject/productfilter/. You should see a page that resembles Figure 10.14. Feel free to experiment with the filter. Figure 10.14. Product filter with Embperl
Next, we come to the page that displays the product detail. When the product link is clicked, the page grabs the query string and prints the information to the user. It starts with: [! # define a title to override the default in base.html sub title { return Product Detail Embperl Style; } # define a bread crumb string to override the default # in base.html sub bread_crumb { return /embperlproject/productfilter/:/productfilter/ :productdetail; } !] The preceding code defines the functions that overide the default definitions in base.html in the parent directory ”defining the title and bread crumb string (wrapped for readability). [- use DBI; # connect to the database $dbh = DBI->connect(DBI:mysql:product_database, apache,LampIsCool); # grab the posted data $prod_id = $fdat{prod_id} ; # make sure the product id is what we expect and the # length is within expected limits unless ($prod_id =~ / ^ [\w]+$/) { $prod_id = ; } if (length($prod_id)>4){ $prod_id = ; } -] The preceding code connects to the database and then grabs the product ID that was sent through the query string and assigned to %fdat . Then the data is tested to be sure it contains only word characters (alphas, digits, or underscores), and the length of the product ID is checked ”good for security! [$ if ($prod_id) $] [# we have a good product id, so query the database and generate #] [# the output #] [- $sth = $dbh->prepare(SELECT products.name, products.category, products.description,products.price, manufacturers.name, manufacturers.address, manufacturers.city, manufacturers.state, manufacturers.zip, manufacturers.man_id FROM manufacturers, products WHERE manufacturers.man_id = products.man_id AND products.prod_id = ? ORDER BY products.category, products.name); $sth->execute($prod_id); ($name,$category,$description,$price,$man_name, $address,$city,$state,$zip,$man_id) = $sth->fetchrow(); -] Next we check to make sure we have a product ID. If so, the database is queried, and we grab most of the data in it for that product ID. Then the result of the query is assigned to several variables, such as $name . [$ if ($name) $] [# $name has a value, so we have a record! display it #] <font color="#ff0000"><b>Product: [+ $prod_id +]</b></font><br> <table border="0" cellspacing="0" cellpadding="0"> <tr><th align="left">Name</th><td> </td><td> [+ $name +]</td></tr> <tr><th align="left">Category</th><td> </td><td> [+ $category +]</td></tr> <tr><th align="left">Description</th><td> </td><td> [+ $description +]</td></tr> <tr><th align="left">Price</th><td> </td><td> $[+ $price +]</td></tr> <tr><th align="left" valign="top">Manufacturer</th><td> </td><td> ID: [+ $man_id +]<br> [+ $man_name +]<br> [+ $address +]<br> [+ $city +], [+ $state +] [+ $zip +] </td></tr> </table> [$ else $] [# oops, the record wasn't in the database, so say so #] Product ID <b>[+ $prod_id +]</b> not found in the database. [$ endif $] The program then checks to see whether it received a name ”if so, $name would have a value. If it has a name, the product information is displayed in the browser. If not, it prints a message saying that the product ID was not found. Finally, we have: [$ else $] [# oops, the user didn't give us a good product ID, so say so #] Please enter a valid product ID. [$ endif $] This else occurs when there is a bad product ID. The program just prints a nice message (we did say please). When you click one of the product links, you should see a page that resembles Figure 10.15. Figure 10.15. Product detail with Embperl
|