Migration from HTML_QuickForm

Migration from HTML_QuickForm – Step-by-step guide for porting your scripts to HTML_QuickForm2

Creating elements

Constructors of all the elements now have the same list of parameters:

string $name

Element name

string|array $attributes

HTML attributes

array $data

Additional element-specific data. A 'label' key in this array is understood by every element and is used for element's label.

Consequently HTML_QuickForm2_Factory::createElement() and HTML_QuickForm2_Container::addElement() that pass their arguments to the element's constructor have fixed signatures, too.

For example, the following HTML_QuickForm code which adds a radio button and a select element to the form

<?php
$form
->addElement('radio''iradTest''Test Radio Buttons:''Check the radio button #1'1,
                  array(
'class' => 'fancyRadio'));
$form->addElement('select''iselTest''Test Select:', array('A'=>'A''B'=>'B''C'=>'C''D'=>'D'), 
                  array(
'size' => 3'multiple' => 'multiple'));
?>

in HTML_QuickForm2 will be transformed to

<?php
$form
->addElement('radio''iradTest', array('class' => 'fancyRadio''value' => 1),
                  array(
'label' => 'Test Radio Buttons:''content' => 'Check the radio button #1'));
$form->addElement('select''iselTest', array('size' => 3'multiple' => 'multiple'),
                  array(
'label' => 'Test Select:''options' => array('A'=>'A''B'=>'B''C'=>'C''D'=>'D'));
?>

Another possibility is using fluent interfaces

<?php
$form
->addElement('radio''iradTest', array('value' => 1))
     ->
addClass('fancyRadio')
     ->
setLabel('Test Radio Buttons:')
     ->
setContent('Check the radio button #1');
$form->addElement('select''iselTest', array('size' => 3'multiple' => 'multiple'))
     ->
setLabel('Test Select:')
     ->
loadOptions(array('A'=>'A''B'=>'B''C'=>'C''D'=>'D'));
?>

which has the benefits of improved readability and code completion in IDEs.

Adding elements to the form

Unlike HTML_QuickForm, where nesting of form elements was limited (elements could be added either directly to the form or to a group added directly to the form) HTML_QuickForm2 supports unlimited nesting. Elements that can contain other elements are subclasses of HTML_QuickForm2_Container which implements DOM-like API: appendChild(), insertBefore(), removeChild(), getElementById(), getElementsByName(). Convenience method addElement() that creates an element of the given type and adds it to the Container is still available, too. Thanks to method overloading you can also perform addEltype() calls where 'eltype' is an element type known to HTML_QuickForm2_Factory:

<?php
$form
->addText('testTextBox', array('style' => 'width: 20ex;'));
?>

Note that HTML_QuickForm2 does not use HTML tables for rendering the form, so there is no equivalent to 'header' element of HTML_QuickForm, fieldsets should be used to group form elements. Also there is no special HTML_QuickForm::addGroup(), groups are treated as any other element (addGroup() will work, though, thanks to abovementioned method overloading). Thus code from HTML_QuickForm

<?php
$form
->addElement('header''personal''Personal Information');
$form->addGroup(array(
    
HTML_QuickForm::createElement('text''first''First''size=10'),
    
HTML_QuickForm::createElement('text''last''Last''size=10')
), 
'name''Name:'',&nbsp;');
?>

can be changed to the following in HTML_QuickForm2

<?php
$fieldset 
$form->addFieldset('personal')->setLabel('Personal Information');
$group    $fieldset->addGroup('name')->setLabel('Name:')->setSeparator(',&nbsp;');
$group->addText('first''size=10', array('label' => 'First'));
$group->addText('last''size=10', array('label' => 'Last'));
?>

Setting default values

Elements' incoming values in HTML_QuickForm2 are kept in an array of DataSources, which are searched for a value in the order they were added to the form. A DataSource containing submit values will be added automatically to that list if the form was submitted. The equivalent to HTML_QuickForm::setDefaults() call

<?php
$form
->setDefaults(array(
    
'foo' => 'default foo value',
    
'bar' => 'default bar value'
));
?>

is the following call to HTML_QuickForm2::addDataSource():

<?php
$form
->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
    
'foo' => 'default foo value',
    
'bar' => 'default bar value'
)));
?>

It is possible to replace the whole array of form's DataSources using HTML_QuickForm2::setDataSources(), so you can change HTML_QuickForm::setConstants() call

<?php
$form
->setConstants(array(
    
'const' => 'this will override submitted value'
));
?>

to

<?php
$datasources 
$form->getDataSources();
array_unshift($datasources, new HTML_QuickForm2_DataSource_Array(array(
    
'const' => 'this will override submitted value'
)));
$form->setDataSources($datasources);
?>

Here we use the fact that new DataSource will be searched before one containing submitted values, so its values will override submitted ones.

Getting the elements' values

While HTML_QuickForm contained several methods for getting form's and elements' values, HTML_QuickForm2 only has getValue() and getRawValue(), the difference being that the former applies filters to the value and the latter does not. There is no longer a special "submit value" stored separately from form element and returned by HTML_QuickForm::getSubmitValue() / HTML_QuickForm::getSubmitValues().

Calling getValue() on an element returns the element's submit value or NULL for elements that do not currently have a submit value or can not have such a value at all. The value also passes "intrinsic validation" to make sure that it could possibly originate from that element (e.g. select will only return values for options that were added to select). This behaviour is closest to that of old HTML_QuickForm::exportValue().

Calling getValue() on a HTML_QuickForm2 object is equivalent to old HTML_QuickForm::exportValues().

Filters

Main differences to filters in HTML_QuickForm:

  • Filters are added to the element rather than to the form and applied to the element's value rather than a separate "submit value".
  • Additional arguments can be passed to filter callbacks.
  • There are separate HTML_QuickForm2_Node::addFilter() and HTML_QuickForm2_Node::addRecursiveFilter() methods. The former will be applied directly to element's value on getValue() call and the latter will be propagated to contained elements if the element is a Container or applied recursively to the value if the element's value is an array.

The following HTML_QuickForm::applyFilter() calls in HTML_QuickForm

<?php
$form
->applyFilter('__ALL__''trim');
$form->applyFilter('amount''intval');
?>

can be converted to the following in HTML_QuickForm2

<?php
$form
->addRecursiveFilter('trim');
$amount->addFilter('intval');
?>

Rules and validation

Main differences to HTML_QuickForm:

  • Rules are added to the element rather than to the form and applied to the element's value rather than a separate "submit value".
  • Rules can be chained with and_() and or_() methods allowing for very complex validation scenarios even with builtin Rules.

Simple addRule() call in HTML_QuickForm

<?php
$form
->addRule('username''Username should be at least 5 symbols long''minlength'5'client');
?>

can be changed to the following in HTML_QuickForm2

<?php
$username
->addRule('minlength''Username should be at least 5 symbols long'5,
                   
HTML_QuickForm2_Rule::CLIENT_SERVER);
?>

There is no longer a special HTML_QuickForm::addGroupRule() method. 'nonempty' / 'required' Rule can directly validate a group or other Container making sure that it contains a given number of nonempty elements, there is also a special 'each' Rule that applies a given template Rule to each element in a Container. Thus the following addGroupRule() calls

<?php
$form
->addGroupRule('phoneNo''Please fill all phone fields''required'null3'client');
$form->addGroupRule('phoneNo''Values must be numeric''regex''/^\\d+$/'3'client');
?>

can be changed to

<?php
$phoneNo
->addRule('required''Please fill all phone fields'3,
                  
HTML_QuickForm2_Rule::CLIENT_SERVER);
$phoneNo->addRule('each''Values must be numeric',
                  
$phoneNo->createRule('regex''''/^\\d+$/'),
                  
HTML_QuickForm2_Rule::CLIENT_SERVER);
?>

There is no longer a special addFormRule() method. You can achieve similar behaviour by creating a subclass of HTML_QuickForm2_Rule, implementing its validateOwner() and setOwnerError() methods and adding an instance of that Rule directly to the form:

<?php
class FormRule extends HTML_QuickForm2_Rule
{
    protected function 
validateOwner()
    {
        
$fooValue $this->owner->getElementById('foo')->getValue();
        
$barValue $this->owner->getElementById('bar')->getValue();

        return 
someComplexCheck($fooValue$barValue);
    }

    protected function 
setOwnerError()
    {
        
$this->owner->getElementById('foo')->setError('foo error');
        
$this->owner->getElementById('bar')->setError('bar error');
    }
}

$form->addRule(new FormRule($form));
?>

Note, though, that it may be easier to use Rule chaining instead in most cases.

File elements no longer require special 'uploadedfile' and 'filename' rules, standard 'required' and 'regex' can be used (client-side as well).

There is now a javascript library provided with the package which contains code necessary for client-side validation to run. This library should be included in the page if you intend to use client-side validation.

Outputting the form

Basic form output in HTML_QuickForm2 is quite easy thanks to a magic __toString() method:

<?php
echo $form;
?>

Under the hood the package uses Renderer setup similar to that of HTML_QuickForm. Currently HTML_QuickForm2 contains ports of Default and Array renderers from HTML_QuickForm, renderers for specific template engines are unlikely to be added to the base package.

Method that accepts a Renderer instance to output the element is now called render() rather than accept() as in HTML_QuickForm.

Default renderer in HTML_QuickForm2 is different from that in HTML_QuickForm:

  • Form is output without HTML tables, similar to HTML_QuickForm_Renderer_Tableless. There is a default CSS file to properly style its output.
  • Template syntax in Default renderer is a bit less verbose.
  • Rules for finding an appropriate template for an element are more complex.

Consider reviewing the examples installed with the package, they contain useful bits on styling the form.

It is also quite easy to output the form manually by iterating over its elements,

<?php
foreach ($form as $element) {
    echo 
'<label for="' $element->getId() . '">' $element->getLabel()
         . 
'</label> ' $element->__toString() . '<br />';
}
?>

but client-side validation rules are built in the course of the render() call, so there is a special Stub renderer to assist with manual output.

If you are using client-side validation or javascript-backed elements like hierselect or repeat, you should take care that necessary javascript libraries are included in the page before the form. Consult the section on Javascript support.