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 an administration page to enable a site editor to change the prices in the products database created in Listing 11.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 a problem 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 11.6 shows the code to create the form.
Listing 11.6 Building an HTML Form Based on Content from a Database
1: <?php 2: $dbh = dba_open( "./data/products", "c", "gdbm" ) 3: or die( "Couldn't open database" ); 4: ?> 5: <html> 6: <head> 7: <title>Listing 11.6 Building an html form based 8: on content from a database</title> 9: </head> 10: <body> 11: <form action="POST"> 12: <table border="1"> 13: <tr> 14: <td>delete</td> 15: <td>product</td> 16: <td>price</td> 17: </tr> 18: <?php 19: $key = dba_firstkey( $dbh ); 20: while ( $key != false ) { 21: $price = dba_fetch( $key, $dbh ); 22: print "<tr><td><input type='checkbox' name=\"delete[]\" "; 23: print "value=\"$key\"></td>"; 24: print "<td>$key</td>"; 25: print "<td> <input type=\"text\" name=\"prices[$key]\" "; 26: print "value=\"$price\"> </td></tr>"; 27: $key = dba_nextkey( $dbh ); 28: } 29: dba_close( $dbh ); 30: ?> 31: <tr> 32: <td> </td> 33: <td><input type="text" name="name_add"></td> 34: <td><input type="text" name="price_add"></td> 35: </tr> 36: <tr> 37: <td colspan=3 align="right"> 38: <input type="submit" value="amend"> 39: </td> 40: </tr> 41: </table> 42: </form> 43: </body> 44: </html>
We start by opening the database as usual (line 2). We then begin an HTML form that points back to the current page (line 11).
Having written some table headers to the screen on lines 13 to 17, we loop through the contents of our database using dba_firstkey() (line 19) and dba_nextkey() (line 27) to get each key in turn, and dba_fetch() on line 21 to extract the value.
In the first table cell of each row, we create a checkbox (line 22). Notice that we give all these the name "delete[]". This instructs PHP to construct an array called $delete of all submitted values that share this name. 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 array with the names of all the database elements that we want to delete.
We then print the element name to the browser on line 24 and create another text field (line 25). This field presents the product price to the user, ready for amendment. We name the field using a similar technique as we did for the previous field. This time, however, we include the name of the database element in the square brackets of the field name. PHP constructs an associative array called $prices from these submitted fields with the element names as keys.
We close the database on line 29 and revert to HTML mode to write the final fields (lines 33 and 34). 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 11.3 shows the output from Listing 11.6.
Figure 11.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.
If the form has been submitted, we know which items we need to delete because a $delete array variable will have been made available. We need to loop through this array and delete the elements whose names it contains.
if ( ! empty( $delete ) ) { while ( list ( $key, $val ) = each ( $delete ) ) { unset( $prices[$val] ); dba_delete( $val, $dbh ); } }
First we test that the $delete array exists and has elements. If the user has only just arrived at the page, or if she has not chosen to delete any items, the variable will not exist. If the variable exists, we can go ahead and loop through it. For each string held in the $delete array, we call dba_delete() removing the element by that name from the database. We also interfere with another array variable. The $prices array contains all the key value pairs in the database, although some of the values might have been changed by the user. If we do not remove the elements, we delete from the database the $price array as well. The next block of code adds them to the database once again.
To update the database according to the user amendments, we have a choice. We could only update 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 only expected to deal with a few products, so we opt to update every element in the database:
if ( ! empty( $prices ) ) { while ( list ( $key, $val ) = each ( $prices ) ) dba_replace( $key, $val, $dbh ); }
We test for the existence of the $prices array. This should contain a new version of the entire database. We 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( $name_add ) && ! empty( $price_add ) ) dba_replace( "$name_add", "$price_add", $dbh );
Instead of testing whether the $name_add and $price_add variables 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 will always be set. They may, however, contain empty strings. We do not want to add empty strings to our database, so we only execute the code to insert new values if neither variable is empty:
if ( ! empty( $name_add ) && ! empty( $price_add ) ) dba_insert("$name_add", "$price_add", $dbh );
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 11.7. You can find the code that handles deletions on lines 5 to 9. The code to update the database is on lines 12 through 15. We handle the insertion of new elements on lines 17 and 18.
Listing 11.7 The Complete Product Maintenance Code
1: <?php 2: $dbh = dba_open( "./data/products", "c", "gdbm" ) 3: or die( "Couldn't open database" ); 4: 5: if ( ! empty( $delete ) ) { 6: while ( list ( $key, $val ) = each ( $delete ) ) { 7: unset( $prices[$val]); 8: dba_delete( $val, $dbh ); 9: } 10: } 11: 12: if ( ! empty( $prices ) ) { 13: while ( list ( $key, $val ) = each ( $prices ) ) 14: dba_replace( $key, $val, $dbh ); 15: } 16: 17: if ( ! empty( $name_add ) && ! empty( $price_add ) ) 18: dba_insert( "$name_add", "$price_add", $dbh ); 19: ?> 20: 21: <html> 22: <head> 23: <title>Listing 11.7 The complete product maintenance code</title> 24: </head> 25: <body> 26: 27: <form action="<? print $PHP_SELF; ?>" action="POST"> 28: 29: <table border="1"> 30: <tr> 31: <td>delete</td> 32: <td>product</td> 33: <td>price</td> 34: </tr> 35: 36: <?php 37: $key = dba_firstkey( $dbh ); 38: while ( $key != false ) { 39: $price = dba_fetch( $key, $dbh ); 40: print "<tr><td><input type='checkbox' name=\"delete[]\" "; 41: print "value=\"$key\"></td>"; 42: print "<td>$key</td>"; 43: print "<td> <input type=\"text\" name=\"prices[$key]\" "; 44: print "value=\"$price\"> </td></tr>"; 45: $key = dba_nextkey( $dbh ); 46: } 47: 48: dba_close( $dbh ); 49: ?> 50: 51: <tr> 52: <td> </td> 53: <td><input type="text" name="name_add"></td> 54: <td><input type="text" name="price_add"></td> 55: </tr> 56: 57: <tr> 58: <td colspan=3 align="right"> 59: <input type="submit" value="amend"> 60: </td> 61: </tr> 62: 63: </table> 64: </form> 65: 66: </body> 67: </html>