Element values and Data sources
Individual elements' values
Each element in HTML_QuickForm2 implements getRawValue() and getValue() methods that return its unfiltered and filtered submit value, respectively, and setValue() that sets the element's display value. These are not always the same, consider
<?php
$text = new HTML_QuickForm2_Element_InputText(
'testText', array('disabled' => 'disabled')
);
$text->setValue('a value');
echo $text . "\n";
var_dump($text->getValue());
?>
which produces
<input type="text" disabled="disabled" name="testText" id="testText-0" value="a value" />
NULL
as disabled elements cannot have a submit value.
If an element can not have a submit value (e.g. reset button, static element) or does not currently have one (e.g. disabled element, unchecked checkbox) then getValue() will return NULL. The value also passes "intrinsic validation" which ensures that it could possibly come from that element:
<?php
$select = HTML_QuickForm2_Factory::createElement('select', 'testSelect',
array('multiple' => 'multiple'))
->loadOptions(array('a' => 'letter A', 'b' => 'letter B', 'c' => 'letter C'));
$select->setValue(array('a', 'z'));
// select will only return values for options that were present in it
var_dump($select->getValue());
?>
will output
array(1) {
[0]=>
string(1) "a"
}
On the other hand, some of the elements cannot have a display value (<input
type="image" />
, file uploads), setValue() will be a
no-op for such elements and they will only get their values (click coordinates and file upload
data, respectively) from submit data sources.
getValue() / getRawValue() also work for Containers and return an array with values of contained elements. setValue() is only implemented for groups, data sources should be used to set the values for the whole form.
Data sources overview
Instance of HTML_QuickForm2 contains an array of Data sources - objects storing elements' incoming values. These values may either originate from HTTP request data (submit values) or be provided by the programmer (default values). Data sources implement HTML_QuickForm2_DataSource interface defining a single getValue() method, which receives element name and returns either element value or NULL if such value is not present.
There is also a HTML_QuickForm2_DataSource_Submit interface that defines an additional
getUpload() method that receives file upload name and returns either file
upload data from $_FILES
array or NULL if it doesn't contain this data.
A data source containing submit values will be added to the list automatically if constructor of HTML_QuickForm2 considers the form submitted. You can add another data source to the list using HTML_QuickForm2::addDataSource() and completely replace the list using HTML_QuickForm2::setDataSources().
An element will try to update its value from data sources in the following cases:
- It is added to the form;
- Its name is changed;
- Form data sources list is changed.
To perform that update it gets the data sources array from the form and iterates over it calling getValue() until a non-NULL value is returned. This value is then used as element's value.
Some of the elements (submit buttons, obviously file uploads) will only consider getting their values from instances of HTML_QuickForm2_DataSource_Submit. Some (e.g. checkboxes) will stop iterating over the array as soon as an instance of HTML_QuickForm2_DataSource_Submit is found, even if its getValue() method returns NULL. Static elements will only consider a datasource if it is not an instance of HTML_QuickForm2_DataSource_Submit.
Implementations of HTML_QuickForm2_DataSource
The package contains several classes implementing the HTML_QuickForm2_DataSource interface, but the only one that should be used directly is HTML_QuickForm2_DataSource_Array, other classes are used internally by the package.
An instance of HTML_QuickForm2_DataSource_Array wraps around an array
with a structure similar to that of superglobal $_GET
/
$_POST
arrays. This wrapper allows searching for values of elements with
complex names:
<?php
$ds = new HTML_QuickForm2_DataSource_Array(array(
'foo' => 'foo value',
'bar' => array('bar 1', 'bar 2'),
'baz' => array('first' => array('second' => 'found a value'))
));
echo $ds->getValue('foo') . "\n";
echo $ds->getValue('bar[1]') . "\n";
echo $ds->getValue('baz[first][second]');
?>
outputs
foo value
bar 2
found a value
Instead of loading data from a database unconditionally and passing it to this data source in an array, it may make sense to create your own data source implementation, which will only perform a query when asked for a value. This way you will probably save a query on form submit, as all values will be found in a submit data source.
Maintaining correct order
A most popular value-related error happens when a programmer uses an element's setValue() method before adding that element to the form:
<?php
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'testDefault' => 'duh!'
)));
$element = HTML_QuickForm2_Factory::createElement('text', 'testDefault')
->setValue('my carefully prepared value');
$form->appendChild($element);
echo $element->getValue();
?>
As described above, the element will immediately try to update its value from form's data sources and overwrite whatever was set on the previous step, so the above results in
duh!
A less obvious problem is adding data sources after elements:
<?php
// an element updates its value from existing (e.g. submit) data sources
$form->addElement('text', 'testDefault');
// it updates its value again
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'testDefault' => 'my carefully prepared value'
)));
// ...and yet again
$form->addDataSource(new HTML_QuickForm2_DataSource_Array(array(
'unrelated' => 'a completely unrelated value'
)));
?>
That code behaves as expected, but performs lots of unneeded work.
To sum it up, a correct order is:
- Add all data sources to the form;
- Add all elements to the form;
- Call setValue() on some elements, if still needed.