DekGenius.com
Previous Section  < Day Day Up >  Next Section

6.6 Putting It All Together

Turning the humble web form into a feature-packed application with data validation, printing default values, and processing the submitted results might seem like an intimidating task. To ease your burden, this section contains a complete example of a program that does it all:

  • Displaying a form, including default values

  • Validating the submitted data

  • Redisplaying the form with error messages and preserved user input if the submitted data isn't valid

  • Processing the submitted data if it is valid

The do-it-all example relies on some helper functions to simplify form element display. These are listed in Example 6-29.

Example 6-29. Form element display helper functions
//print a text box
function input_text($element_name, $values) {
    print '<input type="text" name="' . $element_name .'" value="';
    print htmlentities($values[$element_name]) . '">';
}

//print a submit button
function input_submit($element_name, $label) {
    print '<input type="submit" name="' . $element_name .'" value="';
    print htmlentities($label) .'"/>';
}

//print a textarea
function input_textarea($element_name, $values) {
    print '<textarea name="' . $element_name .'">';
    print htmlentities($values[$element_name]) . '</textarea>';
}

//print a radio button or checkbox
function input_radiocheck($type, $element_name, $values, $element_value) {
    print '<input type="' . $type . '" name="' . $element_name .'" value="' . $element_
value . '" ';
    if ($element_value =  = $values[$element_name]) {
        print ' checked="checked"';
    }
    print '/>';
}

//print a <select> menu
function input_select($element_name, $selected, $options, $multiple = false) {
    // print out the <select> tag
    print '<select name="' . $element_name;
    // if multiple choices are permitted, add the multiple attribute
    // and add a [  ] to the end of the tag name
    if ($multiple) { print '[  ]" multiple="multiple'; }
    print '">';

    // set up the list of things to be selected
    $selected_options = array( );
    if ($multiple) {
        foreach ($selected[$element_name] as $val) {
            $selected_options[$val] = true;
        }
    } else {
        $selected_options[ $selected[$element_name] ] = true;
    }

    // print out the <option> tags
    foreach ($options as $option => $label) {
        print '<option value="' . htmlentities($option) . '"';
        if ($selected_options[$option]) {
            print ' selected="selected"';
        }
        print '>' . htmlentities($label) . '</option>';
    }
    print '</select>';
}

Each helper function in Example 6-29 incorporates the appropriate logic discussed in Section 6.5 for a particular kind of form element. Because the form code in Example 6-30 has a number of different elements, it's easier to put the element display code in functions that are called repeatedly than to duplicate the code each time you need to print a particular element.

The input_text( ) function takes two arguments: the name of the text element and an array of form element values. It prints out an <input type="text"> tag—a single-line text box. If there is an entry in the form element values array that matches the text element's name, that entry is used for the value attribute of the <input type="text"> tag. Any special characters in the value are encoded with htmlentities( ).

The input_submit( ) function prints an <input type="submit"> tag—a submit button. It takes two arguments: the name of the button and the label that should appear on the button.

The input_textarea( ) function takes two arguments like input_text( ): the element name and an array of form element values. Instead of a single-line text box, however, it prints the <textarea></textarea> tag pair for a multiline text box. If there is an entry in the form element values array that matches the element name, that entry is used as the default value of the multiline text box. Special characters in the value are encoded with htmlentities( ).

Both radio buttons and checkboxes are handled by input_radiocheck( ). The first argument to this function is either radio (to display a radio button) or checkbox (to display a checkbox). This determines whether the function prints an <input type="radio"> tag or an <input type="checkbox"> tag. Then comes the element name, the array of form element values, and the value for this particular element. You need to pass both the entire array of form element values and the value for the specific element so the function can see whether the entry in the array for this element matches the passed-in value. For example, Example 6-30 prints three radio buttons named size, each with a different value (small, medium, and large). Only one of those radio buttons can have the checked="checked" attribute set: the one whose entry in the form element values array matches the passed-in value.

The input_select( ) function prints <select> menus. It requires three arguments: the name of the element, an array of form element values, and an array of options to display in the menu. You can also pass true as a fourth argument to allow multiple values to be selected in the menu. The function uses the logic from Examples Example 6-26 and Example 6-27 to build the $selected_options array with one entry for each menu choice that should be marked with the selected="selected" attribute. Then, it loops through the $options array, printing out one <option></option> tag pair for each menu choice.

The code in Example 6-30 relies on the form helper functions and displays a short food-ordering form. When the form is submitted correctly, it shows the results in the browser and emails them to an address defined in process_form( ) (presumably to the chef, so he can start preparing your order). Because the code jumps in and out of PHP mode, it includes the <?php start tag at the beginning of the example and the ?> closing tag at the end to make things clearer.

Example 6-30. A complete form: display with defaults, validation, and processing
<?php
// don't forget to include the code for the form
// helper functions defined in Example 6-29
//
// setup the arrays of choices in the select menus
// these are needed in display_form( ), validate_form( ),
// and process_form( ), so they are declared in the global scope
$sweets = array('puff' => 'Sesame Seed Puff',
                'square' => 'Coconut Milk Gelatin Square',
                'cake' => 'Brown Sugar Cake',
                'ricemeat' => 'Sweet Rice and Meat');

$main_dishes = array('cuke' => 'Braised Sea Cucumber',
                     'stomach' => "Sauteed Pig's Stomach",
                     'tripe' => 'Sauteed Tripe with Wine Sauce',
                     'taro' => 'Stewed Pork with Taro',
                     'giblets' => 'Baked Giblets with Salt', 
                     'abalone' => 'Abalone with Marrow and Duck Feet');

// The main page logic:
// - If the form is submitted, validate and then process or redisplay
// - If it's not submitted, display
if ($_POST['_submit_check']) {
    // If validate_form( ) returns errors, pass them to show_form( )
    if ($form_errors = validate_form( )) {
        show_form($form_errors);
    } else {
        // The submitted data is valid, so process it
        process_form( );
    }
} else {
    // The form wasn't submitted, so display
    show_form( );
}

function show_form($errors = '') {
    // If the form is submitted, get defaults from submitted parameters
    if ($_POST['_submit_check']) {
        $defaults = $_POST;
    } else {
        // Otherwise, set our own defaults: medium size and yes to delivery
        $defaults = array('delivery' => 'yes',
                          'size'     => 'medium');
    }
    
    // If errors were passed in, put them in $error_text (with HTML markup)
    if ($errors) {
        $error_text = '<tr><td>You need to correct the following errors:';
        $error_text .= '</td><td><ul><li>';
        $error_text .= implode('</li><li>',$errors);
        $error_text .= '</li></ul></td></tr>';
    } else {
        // No errors? Then $error_text is blank
        $error_text = '';
    }

    // Jump out of PHP mode to make displaying all the HTML tags easier
?>
<form method="POST" action="<?php print $_SERVER['PHP_SELF']; ?>">
<table>
<?php print $error_text ?>

<tr><td>Your Name:</td>
<td><?php input_text('name', $defaults) ?></td></tr>

<tr><td>Size:</td>
<td><?php input_radiocheck('radio','size', $defaults, 'small');  ?> Small <br/>
<?php input_radiocheck('radio','size', $defaults, 'medium'); ?> Medium <br/>
<?php input_radiocheck('radio','size', $defaults, 'large');  ?> Large
</td></tr>

<tr><td>Pick one sweet item:</td>
<td><?php input_select('sweet', $defaults, $GLOBALS['sweets']); ?>
</td></tr>

<tr><td>Pick two main dishes:</td>
<td>
<?php input_select('main_dish', $defaults, $GLOBALS['main_dishes'], true) ?>
</td></tr>

<tr><td>Do you want your order delivered?</td>
<td><?php input_radiocheck('checkbox','delivery', $defaults, 'yes'); ?> Yes
</td></tr>

<tr><td>Enter any special instructions.<br/>
If you want your order delivered, put your address here:</td>
<td><?php input_textarea('comments', $defaults); ?></td></tr>

<tr><td colspan="2" align="center"><?php input_submit('save','Order'); ?>
</td></tr>

</table>
<input type="hidden" name="_submit_check" value="1"/>
</form>
<?php
      } // The end of show_form( )

function validate_form( ) {
    $errors = array( );

    // name is required
    if (! strlen(trim($_POST['name']))) {
        $errors[  ] = 'Please enter your name.';
    }
    // size is required
    if (($_POST['size'] != 'small') && ($_POST['size'] != 'medium') &&
        ($_POST['size'] != 'large')) {
        $errors[  ] = 'Please select a size.';
    }
    // sweet is required
    if (! array_key_exists($_POST['sweet'], $GLOBALS['sweets'])) {
        $errors[  ] = 'Please select a valid sweet item.';
    }
    // exactly two main dishes required
    if (count($_POST['main_dish']) != 2) {
        $errors[  ] = 'Please select exactly two main dishes.';
    } else {
        // We know there are two main dishes selected, so make sure they are 
        // both valid
        if (! (array_key_exists($_POST['main_dish'][0], $GLOBALS['main_dishes']) &&
               array_key_exists($_POST['main_dish'][1], $GLOBALS['main_dishes']))) {
            $errors[  ] = 'Please select exactly two valid main dishes.';
        }
    }
    // if delivery is checked, then comments must contain something
    if (($_POST['delivery'] =  = 'yes') && (! strlen(trim($_POST['comments'])))) {
        $errors[  ] = 'Please enter your address for delivery.';
    }

    return $errors;
}

function process_form( ) {
    // look up the full names of the sweet and the main dishes in
    // the $GLOBALS['sweets'] and $GLOBALS['main_dishes'] arrays
    $sweet = $GLOBALS['sweets'][ $_POST['sweet'] ];
    $main_dish_1 = $GLOBALS['main_dishes'][ $_POST['main_dish'][0] ];
    $main_dish_2 = $GLOBALS['main_dishes'][ $_POST['main_dish'][1] ];
    if ($_POST['delivery'] =  = 'yes') {
        $delivery = 'do';
    } else {
        $delivery = 'do not';
    }
    // build up the text of the order message
    $message=<<<_ORDER_
Thank you for your order, $_POST[name].
You requested the $_POST[size] size of $sweet, $main_dish_1, and $main_dish_2.
You $delivery want delivery.
_ORDER_;
    if (strlen(trim($_POST['comments']))) {
        $message .= 'Your comments: '.$_POST['comments'];
    }

    // send the message to the chef
    mail('chef@restaurant.example.com', 'New Order', $message);
    // print the message, but encode any HTML entities
    // and turn newlines into <br/> tags
    print nl2br(htmlentities($message));
}
?>

There are four parts to the code in Example 6-30: the code in the global scope at the top of the example, the show_form( ) function, the validate_form( ) function, and the process_form( ) function.

The global scope code does two things. The first is it sets up two arrays that describe the choices in the form's two <select> menus. Because these arrays are used by each of the show_form( ), validate_form( ), and process_form( ) functions, they need to be defined in the global scope. The global code's other task is to process the if( ) statement that decides what to do: display, validate, or process the form.

Displaying the form is accomplished by show_form( ). First, the function makes $defaults an array of default values. If the form has been submitted and is being redisplayed, then the default values come from $_POST. Otherwise, they are explicitly set to yes for the delivery checkbox and medium for the size radio button. Then, show_form( ) prints out a list of errors, if any were passed to it. The HTML list of errors is constructed from the $errors array using implode( ) in a similar technique to the one shown in Example 4-21. Next, show_form( ) jumps out of PHP mode to print the form. Amid the HTML table-formatting tags, it returns to PHP mode repeatedly to call the helper functions that print out the appropriate tags for each form element. The hidden _submit_check element at the end of the form is printed without using a helper function.

The validate_form( ) function builds an array of error messages if the submitted form data doesn't meet appropriate criteria. Note that the checks for size, sweet, and main_dish don't just look to see whether something was submitted for those parameters, but also check whether what was submitted is a valid value for the particular parameter. For size, this means that the submitted value must be small, medium, or large. For sweet and main_dish, this means that the submitted values must be keys in the global $sweets or $main_dishes arrays. Even though the form contains default values, it's still a good idea to validate the input. Someone trying to break into your web site could bypass a regular web browser and construct a request with an arbitrary value that isn't a legitimate choice for the <select> menu or radio button.

Last, process_form( ) takes action when the form is submitted with valid data. It builds a string, $message, that contains a description of the submitted order. Then it emails $message to chef@restaurant.example.com and prints it. The built-in mail( ) function sends the email message. (See Section 13.5 for more details on mail( ).) Before printing $message, process_form( ) passes it through two functions. The first is htmlentities( ), which, as you've already seen, encodes any special characters as HTML entities. The second is nl2br( ), which turns any newlines in $message into HTML <br> tags. Turning newlines into <br> tags makes the line breaks in the message display properly in a web browser.

    Previous Section  < Day Day Up >  Next Section