[ Team LiB ] Previous Section Next Section

An Example

We now have enough information to build an example using some of the techniques discussed in this hour. Our brief is to build a quick and dirty script to enable a site editor to change the prices in the products database created in Listing 12.2. The administrator should also be able to remove elements from the database and add new ones. The page will not be hosted on a publicly available server, so security is not an issue for this project.

First, we must build a form that incorporates all the elements in the database. The user will be able to change any price using a text field and choose which items to delete using a check box. She will also have two text fields for adding a new item to the database. Listing 12.6 shows the code to create the form.

Listing 12.6 Building an HTML Form Based on Content from a Database
 2:   "-//W3C//DTD XHTML 1.0 Strict//EN"
 3:   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
 4: <html>
 5: <head>
 6: <title>Listing 12.6 Building an HTML Form Based
 7:    on Content From a Database</title>
 8: </head>
 9: <body>
10: <form method="post" action="<?php print $SERVER['PHP_SELF'] ?>">
11: <table border="1">
12: <tr>
13: <td>delete</td>
14: <td>product</td>
15: <td>price</td>
16: </tr>
17: <?php
18: $dbh = dba_open( "./data/products", "c", "gdbm" )
19:       or die( "Couldn't open database" );
20: $key = dba_firstkey( $dbh );
21: while ( $key != false ) {
22:   $price = dba_fetch( $key, $dbh );
24:   print <<<LIST
25:   <tr><td>
26:     <input type="checkbox" name="delete[]" value="$key" />
27:   </td><td> $key </td><td>
28:     <input type="text" name="prices[$key]" value="$price" />
29:   </td></tr>
30: LIST;
32:   $key = dba_nextkey( $dbh );
33: }
34: dba_close( $dbh );
35: ?>
36: <tr>
37: <td> </td>
38: <td><input type="text" name="name_add" /></td>
39: <td><input type="text" name="price_add" /></td>
40: </tr>
41: <tr>
42: <td colspan="3" align="right">
43: <input type="submit" value="amend" />
44: </td>
45: </tr>
46: </table>
47: </form>
48: </body>
49: </html>


In Listing 12.6, we use the 'heredoc' print statement syntax. Using this syntax, print statements look like this:

    print <<<CHUNK
contents here

The word CHUNK in the previous fragment is arbitrary. You can use your own word, as long as you are consistent. The document terminator (the second instance of CHUNK) should be flush with the left margin and must be followed by a semicolon and no spaces. This syntax makes printing multiple lines of text easy. Variables are substituted as they are in double-quoted strings with the exception that you do not have to escape double-quote characters within the string.

We begin an HTML form that points back to the current page (line 10).

Having written some table headers to the screen on lines 1216, we open the database and loop through the contents of it using dba_firstkey() (line 20) and dba_nextkey() (line 32) to get each key in turn. We then use dba_fetch() on line 22 to extract the value.

In the first table cell of each row, we create a check box (line 25). Notice that we give all these the name "delete[]". This instructs PHP to construct an array element called delete containing all submitted values that share this name. The delete element is stored in the $_POST array. We use the database element name (stored in $key) as the value for each check box. When the form is submitted, therefore, we should have a delete element available to us in the built-in $_POST array. The $_POST ['delete'] array will contain the names of all the database elements we want to delete.

We then print the element name to the browser on line 27 and create another text field on line 29. This field presents the product price to the user, ready for amendment. We name the field 'prices' followed by square brackets containing the name of the database element. When the form is submitted, PHP adds an associative array element called prices to the $_POST superglobal. Each element in the $_POST['prices'] array will be indexed by a database key, and its value will be the user-submitted price data.

We close the database (line 34) and write the final fields (lines 38 and 39). These allow the user to add new product and price combinations. Only two fields are required, and we give them the names name_add and price_add.

Figure 12.3 shows the output from Listing 12.6.

Figure 12.3. Building an HTML form based on content from a database.


Now that we have created the form, we need to write code to deal with the user input. This is not as difficult as it sounds. There are three possible actions we can take. First, we can delete items from the database; second, we can amend prices in the database; and third, we can add new elements to the database.

User-submitted data is available to us in the superglobal $_POST array. We will work directly with this array in most instances, but we need to save one element to an array variable:

$prices = $_POST['prices'];

We might need to remove elements from the $prices array. If you need to manipulate user input, it is generally better to work with a local version than directly on the built-in global.

If the form has been submitted, we know which items we need to delete because a $_POST['delete'] array variable will have been made available. We need to loop through this array and delete the elements whose names it contains, like so:

if ( ! empty( $_POST['delete'] ) ) {
  foreach( $_POST['delete'] as $val ) {
    unset( $prices[$val]);
    dba_delete( $val, $dbh );

First, we test that the $_POST['delete'] array exists and has elements. If the user has only just arrived at the page or has not chosen to delete any items, the $_POST array will not have a 'delete' element. If the element exists, we can loop through it. For each string held in the $_POST['delete'] array, we call dba_delete(), removing the element by that name from the database. We also remove the element of the same name from the $prices array.

To update the database according to the user amendments, we have a choice. We could update only those elements that the user has elected to change. We would choose this option if we expected many users to be using the script at the same time or if the database was likely to grow significantly. As it is, this script will be run by a single administrator and is expected to deal with only a few products, so we opt to update every element in the database:

if ( ! empty( $prices ) ) {
  foreach ( $prices as $key=>$val ) {
    dba_replace( $key, (float)$val, $dbh );

We test for the existence of the $prices array, which should contain a new version of the entire database. We then loop through the array, calling dba_replace() for each of its elements.

Finally, we need to check whether the user has submitted a new product for inclusion in the database:

if ( ! empty( $_POST['name_add'] ) && ! empty( $_POST['price_add'] ) ) {
  dba_insert( $_POST['name_add'], (float)$_POST['price_add'], $dbh );

Instead of testing whether $_POST['name_add'] and $_POST['price_add'] are set, we test whether they are empty. This is a subtle but important difference. When the user submits the form we have built, these variables are always set. They can, however, contain empty strings. We do not want to add empty strings to our database, so we execute the code to insert new values only if neither variable is empty.

We use dba_insert() rather than dba_replace() to guard against the user inadvertently overwriting an element that has already been defined.

You can see the complete code in Listing 12.7; you can find the code that handles deletions on lines 610. The code to update the database is on lines 1317. We handle the insertion of new elements on lines 1921.

Listing 12.7 The Complete Product Maintenance Code
 1: <?php
 2: $dbh = dba_open( "./data/products", "c", "gdbm" )
 3:       or die( "Couldn't open database" );
 4: $prices = $_POST['prices'];
 6: if ( ! empty( $_POST['delete'] ) ) {
 7:   foreach( $_POST['delete'] as $val ) {
 8:     unset( $prices[$val]);
 9:     dba_delete( $val, $dbh );
10:   }
11: }
13: if ( ! empty( $prices ) ) {
14:   foreach ( $prices as $key=>$val ) {
15:     dba_replace( $key, (float)$val, $dbh );
16:   }
17: }
19: if ( ! empty( $_POST['name_add'] ) && ! empty( $_POST['price_add'] ) ) {
20:   dba_insert( $_POST['name_add'], (float)$_POST['price_add'], $dbh );
21: }
22: ?>
24:   "-//W3C//DTD XHTML 1.0 Strict//EN"
25:   "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
26: <html>
27: <head>
28: <title>Listing 12.7 The Complete Product Maintenance Code</title>
29: </head>
30: <body>
31: <form method="post" action="<?php print $SERVER['PHP_SELF'] ?>">
32: <table border="1">
33: <tr>
34: <td>delete</td>
35: <td>product</td>
36: <td>price</td>
37: </tr>
38: <?php
39: $key = dba_firstkey( $dbh );
40: while ( $key != false ) {
41:   $price = dba_fetch( $key, $dbh );
43:   print <<<LIST
44:   <tr><td>
45:     <input type="checkbox" name="delete[]" value="$key" />
46:   </td><td> $key </td><td>
47:     <input type="text" name="prices[$key]" value="$price" />
48:   </td></tr>
49: LIST;
51:   $key = dba_nextkey( $dbh );
52: }
53: dba_close( $dbh );
54: ?>
55: <tr>
56: <td> </td>
57: <td><input type="text" name="name_add" /></td>
58: <td><input type="text" name="price_add" /></td>
59: </tr>
60: <tr>
61: <td colspan="3" align="right">
62: <input type="submit" value="amend" />
63: </td>
64: </tr>
65: </table>
66: </form>
67: </body>
68: </html>
    [ Team LiB ] Previous Section Next Section