I recently needed to develop an HTML form to input and edit contact details in a database with the usual name, address, telephone, and web page fields. This very common task is made more complex because of the need to validate the input data according to various rules and to notify the user if the validation process fails. I'm also always keen to separate the presentation logic (typically HTML) from the programming logic (PHP, in this case).
Enter PEAR, the PHP Extension and Application Repository, which provides an
ever-growing number of classes to help with PHP programming. (See "An Introduction to
PEAR" and "A
Detailed Look at PEAR.") In this article, we will take a look at PEAR's
HTML_QuickForm class.
To start, I created a simple form with a heading, a text box, and submit and reset buttons, as follows:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html"
charset="UTF-8">
<title>Testing PEAR HTML_QuickForm</title>
</head>
<body>
<?php
require_once "HTML/QuickForm.php";
$form = new HTML_QuickForm('frmTest', 'get');
$form->addElement('header', 'hdrQuickformtest',
'QuickForm Example 1');
$form->addElement('text', 'txtName',
'What is your name?');
$form->addElement('reset', 'btnClear',
'Clear');
$form->addElement('submit', 'btnSubmit',
'Submit');
$form->display();
?>
</body>
</html>
Taking the PHP code line by line, the first line loads the class definitions
for QuickForm.
|
Related Reading
|
Next, I instantiate an HTML_QuickForm object. This defines the name of the
form (frmTest) and the method used to pass the form variables back
to the PHP script (get, the alternative being post).
By default, the form's action -- the URI to which to submit the form data
-- is the same URI that displayed the form in the first place. This may
seem unusual at first, but it is actually very useful, as we shall see.
The next four lines add four elements to the form. Elements are
either orthodox HTML form elements, such as text boxes or radio buttons, or
PEAR-specific pseudo-elements. The first parameter to the
addElement method is mandatory; it defines the element type. Here,
header is a pseudo-element that just places a heading bar on the
form. The second parameter of each element is a name by which code can refer to
it, if necessary. In the case of the header, the name is
hdrQuickformtest. The third parameter is the text label associated
with each element. For the header, this is the text to display. For the
text element, a text box, it is the prompt before the text box. For
the two buttons, it is the text on the button face.
Finally, the line $form->display(); renders the form to the
screen.
When displayed, this page shows a header, a text box and two buttons: so far,
so good. However, clicking the submit button appears to do
nothing. The next step is to add the code to process the form after
submission.
QuickForm TheoryHTML_QuickForm uses the concept of freezing elements of the form.
The user cannot edit a frozen element. HTML_QuickForm enforces this by changing
the rendering of the element. In the case of a frozen text box, the contents
will render as simple text, thus denying the user the opportunity to edit the
text value.
HTML_QuickForm also introduces the concept of validating a form. To
validate, a form must have received one or more parameters via GET or POST, and
all validation rules must succeed.
To test how freezing and validating work, I made a small change to the form
by adding an if statement to call the form's validate
method before displaying it. This will return true if the URI
which called this code includes at least one POST/GET parameter, and all rules
(of which there are none in this example) succeed. When this page first
displays, there are no submitted parameters; validate returns
false, and the form displays as before. The PHP code is:
<?php
require_once "HTML/QuickForm.php";
$form = new HTML_QuickForm('frmTest', 'get');
$form->addElement('header', 'hdrQuickformtest',
'QuickForm Example 2');
$form->addElement('text', 'txtName',
'What is your name?');
$form->addElement('reset', 'btnClear',
'Clear');
$form->addElement('submit', 'btnSubmit',
'Submit');
if ($form->validate()) {
# If the form validates then freeze the data
$form->freeze();
}
$form->display();
?>
When a user clicks the Submit button, the action parameter of
the form (by default, the URI that displayed the form in the first place) will
now have a GET parameter appended in the form
?txtName=myname&btnSubmit=Submit. This means that the
validation will succeed, freezing the form elements, which in turn means that
the form will next render all elements in their non-editable forms.
This proved the basic operation of HTML_QuickForm, but I wanted to process
the submitted data rather than just display it. This requires one additional
method. For proof-of-concept purposes, I wrote a small function to display the
data:
<?php
require_once "HTML/QuickForm.php";
$form = new HTML_QuickForm('frmTest', 'get');
$form->addElement('header', 'hdrQuickformtest',
'QuickForm Example 3');
$form->addElement('text', 'txtName',
'What is your name?');
$form->addElement('reset', 'btnClear',
'Clear');
$form->addElement('submit', 'btnSubmit',
'Submit');
if ($form->validate()) {
# If the form validates, freeze and process the data
$form->freeze();
$form->process("process_data", false);
}
else {
$form->display();
}
function process_data ($values) {
echo "<pre>";
foreach ($values as $key=>$value) {
echo $key."=".$value."<br>";
}
echo "</pre>";
}
?>
In this example, the form will display only if the validation fails. When
the validation succeeds, the process method defines which function
to call. That function takes two parameters; the first is all of the
submitted data in the form of an associative array. The second parameter to the
process method, false, relates to uploaded files, so
it does not apply in this case. In the above example, the
process_data function merely deconstructs and displays the passed
array, although in a production environment it would process the data as
required.
Now I had a form that could solicit data from the user and pass it to
another function for processing. However, this is nothing that an ordinary HTML
form cannot do. The value of using PEAR's HTML_QuickForm comes with validating
and applying automated filtering to the data, which is what I set out to do
next.
|
In the simplest case, I wanted to ensure that the user really had entered a
name before clicking the Submit button. To accomplish this, I called an
addRule method of the form:
$form->addRule('txtName', 'Please enter your name', 'required');
The first argument is the name of the element to apply the rule to, the
second is the error text to display if the rule fails, and the third is the
type of rule to apply. This will require a value for txtName;
it must not be blank. When the form displays, it will indicate that this is a
required field. By default, a red asterisk will appear next to the field that
refers to a note at the foot of the form. If txtName is blank,
the validate method call will return false. This in
turn will cause the form to redisplay, but now the error text (the second
parameter to the addRule method) will display above the field
that failed the validation.
By adding that one line of code, we can show the user which fields are
mandatory, we can check that the user has filled them in, and we can provide
feedback to the user in the event that they have not filled them all in. There
are other built-in rules, including: maxlength,
minlength, rangelength, email,
regex, lettersonly, alphanumeric,
numeric, nopunctuation, nonzero, and
compare.
Suppose, however, that the user merely inserts a space in the
txtName field. The validation will still pass because the field is
not blank, but this is hardly what we want. There are other rules we could use
-- for example, the regex rule checks the input against a
regular expression, but that is complex way to solve a simple problem.
A better way approach is to use filters. A filter can be either the
name of a user-supplied function or the name of a PHP built-in function.
QuickForm will send the contents of the text field to this function before
validation. In this case, the PHP function trim does precisely
what we want by removing all leading and trailing spaces. Here it is in
action:
$form->addRule('txtName', 'Please enter your name', 'required');
$form->applyFilter('txtName', 'trim');
The applyFilter method takes two arguments: the name of the
element to apply the filter to, and the name of the filter, as described above.
The element name can be the pseudo-element __ALL__, which, as you
might expect, applies that filter to all elements.
One of the elements on the form I designed was a checkbox that indicated
whether the person wanted to subscribe to a mailing list. The validation
required here is not so simple: the EmailAddress field should contain either a
valid email address or nothing, but if the user has checked the MailingList
checkbox, the EmailAddress field must not be blank.
We can achieve this with a different built-in rule in conjunction with a user-defined validation rule, as follows (see Example 1).
There's quite a lot extra here. First, I've added a couple of new elements:
a text box for the user's email address and an advcheckbox for
requesting a newsletter. The advcheckbox is a special
HTML_QuickForm element. The problem with an ordinary checkbox is that it
returns no value at all if left unchecked, making it more difficult to check in
code; however, the advanced checkbox will always return a value, either
TRUE or FALSE, as appropriate.
I've added a filter rule to remove leading and trailing spaces from all data
entered. There are two element-specific rules: one to ensure that the user has
entered a name, and the other, email, to ensure that any email
address entered matches a valid format. If the email address is blank, the
validation check will still pass; if an email address was required here,
then a second rule, required, would also apply to the field. As
it stands, the validation routine will only check that the format of the email
address is legal. By specifying an additional parameter, the validation routine
can also check whether the address given really exists.
Finally, we have a new method, addFormRule. This takes a single
argument, the name of a user-written function that will help validate the form.
This function, newcontact_validate, takes a single parameter in
the form of an associative array consisting of
element-name=>element-value pairs. This function returns either
TRUE, indicating that validation has passed, or an associative array of
invalid-element-name=>error-message pairs.
In this particular example, we only check that the user has supplied an
email address if they have elected to receive a newsletter. However, the format
of this function is generic enough to perform more complex validations. In the
example shown, we first define an array in which to store any errors we find.
We then carry out the checks, adding an element name and error message to the
errors array for each validation error found. At the end of the
function, we check the errors array and either return it if there
are errors, or return true if everything validated. Any errors
returned by the addFormRule function display in the same way as
built-in validation rule failures.
My form was almost complete. It worked well for entering details of new
contacts, but I also wanted to use it to edit details of existing contacts. I
needed a way to populate the fields with existing data. To accomplish this, I
used the setDefaults method (see Example
2).
There are a few changes here, starting with the definition of some user data
in the array $user. I'd normally retrieve this from a database,
but for clarity I have hard-coded it here. The form definition resembles the
previous version, but I have changed the element names to match the column
names in my database. For example, txtName has become simply
name. There is also a new element, hidden. As the
name implies, this is a hidden element on the form. In this case, it stores the
key field in the database to use in the resulting SQL UPDATE statement.
The final change is the setDefaults method. This takes a single
argument, an associative array of element-name=>default-value
pairs. Using the same name for both the database fields and the element names
on the form greatly simplifies the population of default values and the later
building of the SQL UPDATE statement. Any element that does not appear in the
array passed with setDefaults retains its default (typically
blank) value, so the same code can both create contacts (passing an empty
defaults array) and edit contacts.
I now have a form which is in daily use. It consists of pure PHP code with none of the lack of clarity which can arise from mixing PHP and HTML code in the same file. My users use the same code both to create and edit contacts, which makes the application more consistent in its appearance and easier to maintain. All of the input fields are validated with very little code, and users receive good feedback if any of the validations fail.
You can find out more about PEAR at the PEAR web site and from the online PEAR manual. In this article,
I have only scratched the surface of what can be achieved by PEAR's
HTML_QuickForm classes. There's a lot more power available there. With the
addition of templates, the resulting forms can look very professional --
but perhaps that is a subject for another time.
Return to the PHP DevCenter
Copyright © 2007 O'Reilly Media, Inc.