In this section, we use sessions to improve the user interaction with the phonebook details form developed in Chapter 8. The improvements focus on the interaction when the form is submitted and fields fail validation. We modify the scripts to:
10.3.1 Improving the Phonebook Details FormWe designed several phonebook scripts in Chapter 8. In Example 8-7, the form generated by the script collects values to create a new entry. The script shown in Example 8-9 performs simple server-side validation of the form data, and inserts a row in the phonebook table if there are no errors. We improve these scripts in this section, but the techniques we show you can be easily adapted to the other phonebook scripts or to any form pages you've authored. If validation fails, the script shown in Example 8-9 generates a page to display the errors to the user, and the user then follows a hypertext link back to the phonebook entry form to reenter the fields. The solution provided by Example 8-7 and Example 8-9 has three problems:
In this section we develop scripts that make use of session variables to solve these problems. Rather than displaying the error messages on a page generated by the validation script, we show you how to display the errors in red above the appropriate input fields on the data entry form, as shown in Figure 10-3 (the red text appears as a light gray in the figure). Figure 10-3. The phonebook entry form showing error messagesFigure 10-4 shows how the improved form and the validate scripts interact with session management to communicate errors and submitted fields. Figure 10-4. Phonebook entry form and validationBecause the validation script processes the fields collected in the phonebook form and generates any associated errors, we look at the changes required for that script first. 10.3.2 The Validation ScriptWe begin the improvements to the validation script with the changes required to support an error message session variable and then discuss how to record the values to pass back to the phonebook entry form generation code. We then show you the complete structure of the modified validation script. 10.3.2.1 Improving error messagesThe validation script checks each variable submitted from the phonebook form to make sure data has been entered. The script shown in Example 8-9 builds up a template by adding blocks as errors are found. In the modified script we use in this case study, an associative array is registered instead to hold error messages associated with each field, providing greater flexibility when displaying the error messages. First, we need to initialize the session with a call to session_start( ) and set up $errors to hold an array of errors: // Initialize a session session_start( ); // Set-up an empty $errors array to hold errors $errors = array( ); The script then checks each variable and adds an error message to the associative array $errors if an error is encountered. The error message array is indexed by the name of the field being checked. For example, the validation of the surname is coded as: // Validate the Surname if (empty($surname)) $errors["surname"] = "The surname field cannot be blank."; Once all the fields have been validated, the size of the $errors array is tested to determine if any errors were encountered. If the size of the array is zero, we create or update the row as before, otherwise we carry out several steps as shown in the following fragment: // Now the script has finished the validation, check if // there were any errors if (count($errors)) { // Set up a $lastformVars array to store the previously-entered data $lastformVars = array( ); $lastFormVars["surname"] = $surname; $lastFormVars["firstname"] = $firstname; $lastFormVars["phone"] = $phone; // Save the array as a session variable $_SESSION["lastFormVars"] = $lastFormVars; // Store the $errors array as a session variable $_SESSION["errors"] = $errors; // Relocate to the phonebook form header("Location: example.10-5.php"); exit; } The setup and use of the lastFormVars array is discussed in the next section. The remainder of the fragment saves the $errors array as a session variable and relocates to the phonebook entry form script. In Example 8-9, the script itself displays any errors, and because the request contains variables in a POST method request, the error page suffers from the reload problem discussed in Chapter 8. The script has to display the errors immediately, in isolation, because without sessions there is no convenient way to save the errors and retrieve them again when displaying the form. In the validation script developed here, we relocate to the phonebook entry form (shown later in Example 10-5) and let it display the errors held in the session variable $_SESSION["errors"]. 10.3.2.2 Saving last-entered values as a session variableWe now show you how to pass the field data from the validation script back to the phonebook entry form, so the user does not have to re-key data after an error occurs. The fields are passed back the session array variable lastFormVars. The following code fragment saves each value entered into the form into a $lastFormVars array, indexed by the name of the variable. The $lastFormVars array is then saved as the session variable $_SESSION["lastFormVars"]. // Set up a $lastformVars array to store the previously-entered data $lastformVars = array( ); $lastFormVars["surname"] = $surname; $lastFormVars["firstname"] = $firstname; $lastFormVars["phone"] = $phone; // Save the array as a session variable $_SESSION["lastFormVars"] = $lastFormVars; When the modified form is run, the most recent values entered from the session variable $_SESSION["lastFormVars"] are shown. The final change needed is to destroy the session when the script successfully saves a row in the phonebook table: // Destroy the session session_destroy( ); However, your application may make use of the session for other purposes and you may not want to destroy the session at this point. If this is the case then you should unset the variables used in these scripts. // Clean up the lastFormVars from the session store unset($_SESSION["lastFormVars"]); 10.3.2.3 The final validation scriptExample 10-4 shows the final validation script derived from Example 8-9. Example 10-4. The complete validation script derived from Example 8-9<?php require 'db.inc'; if (!($connection = @ mysql_pconnect("localhost", "fred", "shhh"))) die("Could not connect to database"); $firstname = mysqlclean($_POST, "firstname", 50, $connection); $surname = mysqlclean($_POST, "surname", 50, $connection); $phone = mysqlclean($_POST, "phone", 20, $connection); // Initialize a session session_start( ); // Set-up an empty $errors array to hold errors $errors = array( ); // Validate the Firstname if (empty($firstname)) $errors["firstname"] = "The firstname field cannot be blank."; // Validate the Surname if (empty($surname)) $errors["surname"] = "The surname field cannot be blank."; // Validate the Phone number. It must have the correct format $validPhoneExpr = "^([0-9]{2,3}[ ]?)?[0-9]{4}[ ]?[0-9]{4}$"; if (empty($phone) || !ereg($validPhoneExpr, $phone)) $errors["phone"] = "The phone number must be 8 digits in length,. with an optional 2 or 3 digit area code"; // Now the script has finished the validation, check if // there were any errors if (count($errors)) { // Set up a $lastformVars array to store // the previously-entered data $lastformVars = array( ); $lastFormVars["surname"] = $surname; $lastFormVars["firstname"] = $firstname; $lastFormVars["phone"] = $phone; // Save the array as a session variable $_SESSION["lastFormVars"] = $lastFormVars; // Store the $errors array as a session variable $_SESSION["errors"] = $errors; // Relocate to the phonebook form header("Location: example.10-5.php"); exit; } // If we made it here, then the data is valid if (!mysql_select_db("telephone", $connection)) showerror( ); // Insert the new phonebook entry $query = "INSERT INTO phonebook VALUES (NULL, '{$surname}', '{$firstname}', '{$phone}')"; if (!(@ mysql_query ($query, $connection))) showerror( ); // Find out the phonebook_id of the new entry $phonebook_id = mysql_insert_id( ); // Destroy the session session_destroy( ); // Show the phonebook receipt header("Location: example.8-5.php?status=T&phonebook_id={$phonebook_id}"); ?> 10.3.3 The Phonebook Entry Form ScriptNow let's turn to the changes required for the script that generates the phonebook entry form shown in Example 8-7. In the last section, we set up two session variables: $_SESSION["errors"] to hold an associative array of error messages found by the validation script, and $_SESSION["lastFormVars"] to hold an associative array filled with the form values. Both session variables are read and incorporated into a new form in this section. 10.3.3.1 Displaying previously entered form valuesIn our update phonebook details form in Example 8-11, we read data from the phonebook table and display it in the input widgets so that the user can amend it. The form uses the array $row to populate the data entry fields from a phonebook row when editing an existing entry in the database. In this section, we adapt this approach to displaying previously-entered data that has failed validation. Adapting the approach from Example 8-11 to our sessions-based script is straightforward. Consider the following fragment: $row = array( ); // Has previous data been entered? // If so, initialize $row from $_SESSION["lastFormVars"] if (isset($_SESSION["lastFormVars"])) { $row = $_SESSION["lastFormVars"]; $template->setVariable("MESSAGE", "Please correct the errors shown below"); $template->setVariable("SUBMITVALUE", "Try again"); } If the $_SESSION["lastFormVars"] variable is set, $row is set to $_SESSION["lastFormVars"], and a message and submit button value set to inform the user that errors have occurred. Then, for each widget in the form, the script displays the value the user previously entered (or an empty widget if no previous value was supplied): if (!empty($row)) $template->setVariable("MINPUTVALUE", $row["firstname"]); else $template->setVariable("MINPUTVALUE", ""); 10.3.3.2 Displaying error messagesTo display the error messages above the input widgets, we've modified our phonebook template; the phonebook template is discussed in more detail in Chapter 8. It now includes the following fragment: <!-- BEGIN mandatoryinput --> <tr> <!-- BEGIN mandatoryerror --> <td> <td><font color="red">{MINPUTERROR}</font> </tr> <tr> <!-- END mandatoryerror --> <td><font color="red">{MINPUTTEXT}:</font></td> <td> <input type="text" name="{MINPUTNAME}" value="{MINPUTVALUE}" size={MINPUTSIZE}> </td> </tr> <!-- END mandatoryinput --> The mandatoryerror block is an optional block that's included before the input element and is used to show an error message in a red font using the placeholder MINPUTERROR. If no error occurs, we don't set MINPUTERROR and don't use the mandatoryerror block. To decide whether to display an error message or not, we check the contents of the $_SESSION["errors"] array. If there's an entry for the input name in the associative array of error messages, we use the mandatoryerror block and display the message. Here's an example for the firstname element: if (!empty($_SESSION["errors"]["firstname"])) { $template->setCurrentBlock("mandatoryerror"); $template->setVariable("MINPUTERROR", $_SESSION["errors"]["firstname"]); $template->parseCurrentBlock("mandatoryerror"); } Figure 10-4 shows the final results: a form with error messages placed over the corresponding fields. 10.3.3.3 The final phonebook entry scriptExample 10-5 shows the complete data entry script, derived from Example 8-7, that displays the previous form values and the error messages held in session variables. Example 10-6 shows the template. Example 10-5. Phonebook entry form derived from Example 8-7<?php require 'db.inc'; require_once "HTML/Template/ITX.php"; if (!($connection = @ mysql_connect("localhost", "fred", "shhh"))) die("Could not connect to database"); session_start( ); $template = new HTML_Template_ITX("./templates"); $template->loadTemplatefile("example.10-6.tpl", true, true); $row = array( ); // Has previous data been entered? // If so, initialize $row from $_SESSION["lastFormVars"] if (isset($_SESSION["lastFormVars"])) { $row = $_SESSION["lastFormVars"]; $template->setVariable("MESSAGE", "Please correct the errors shown below"); $template->setVariable("SUBMITVALUE", "Try again"); } else { // If they're not correcting an error show a // "fill in the details" message $template->setVariable("MESSAGE", "Please fill in the details below to add an entry"); $template->setVariable("SUBMITVALUE", "Add Now!"); } $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "First name"); $template->setVariable("MINPUTNAME", "firstname"); if (!empty($row)) $template->setVariable("MINPUTVALUE", $row["firstname"]); else $template->setVariable("MINPUTVALUE", ""); if (!empty($_SESSION["errors"]["firstname"])) { $template->setCurrentBlock("mandatoryerror"); $template->setVariable("MINPUTERROR", $_SESSION["errors"]["firstname"]); $template->parseCurrentBlock("mandatoryerror"); } $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTSIZE", 50); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "Surname"); $template->setVariable("MINPUTNAME", "surname"); if (!empty($row)) $template->setVariable("MINPUTVALUE", $row["surname"]); else $template->setVariable("MINPUTVALUE", ""); if (!empty($_SESSION["errors"]["surname"])) { $template->setCurrentBlock("mandatoryerror"); $template->setVariable("MINPUTERROR", $_SESSION["errors"]["surname"]); $template->parseCurrentBlock("mandatoryerror"); } $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTSIZE", 50); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTTEXT", "Phone"); $template->setVariable("MINPUTNAME", "phone"); if (!empty($row)) $template->setVariable("MINPUTVALUE", $row["phone"]); else $template->setVariable("MINPUTVALUE", ""); if (!empty($_SESSION["errors"]["phone"])) { $template->setCurrentBlock("mandatoryerror"); $template->setVariable("MINPUTERROR", $_SESSION["errors"]["phone"]); $template->parseCurrentBlock("mandatoryerror"); } $template->setCurrentBlock("mandatoryinput"); $template->setVariable("MINPUTSIZE", 20); $template->parseCurrentBlock("mandatoryinput"); $template->setCurrentBlock( ); $template->parseCurrentBlock( ); $template->show( ); ?> Example 10-6. The template to display the phonebook form<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html401/loose.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"> <title>Phonebook Details</title> </head> <body bgcolor="white"> <form method="post" action="example.10-4.php"> <h1>Phonebook Details</h1> <h2>{MESSAGE}. Fields shown in <font color="red">red</font> are mandatory.</h2> <table> <!-- BEGIN mandatoryinput --> <tr> <!-- BEGIN mandatoryerror --> <td> <td><font color="red">{MINPUTERROR}</font> </tr> <tr> <!-- END mandatoryerror --> <td><font color="red">{MINPUTTEXT}:</font></td> <td> <input type="text" name="{MINPUTNAME}" value="{MINPUTVALUE}" size={MINPUTSIZE}> </td> </tr> <!-- END mandatoryinput --> <tr> <td><input type="submit" value="{SUBMITVALUE}"></td> </tr> </table> </form> </body> </html> |