I've been working on
for the past few weeks, and it's nearing release readiness. There are a
number of features that Cal didn't cover in his
DevZone coverage
(in part because some of them weren't yet complete) that I'd like to
showcase, including:
- Internationalization
- Element grouping for display and logistical purposes
- Array support
This post will serve primarily as a high-level overview of some of these
features; if you're looking for more in-depth coverage, please review the
unit tests.
Internationalization
When using form components in many libraries, internationalization (i18n) is
often tricky to accomplish. There are many potential translation targets:
labels, submit and reset buttons, and error messages all potentially
need to be treated.
Zend_Form allows setting a translation object at both the element and form
level, and also allows setting a default translation object for all forms
and elements. I personally feel this latter is the most flexible; in most
projects, you'll have a single set of translation files, so why not simply
re-use the same object throughout?
// Create your translation object
$translate = new Zend_Translate(...);
// Set it as the default object for all forms and elements:
Zend_Form::setDefaultTranslator($translate);
What do you get for this?
- Legends. If a fieldset legend has a translation available, it will be
translated.
- Labels. If the label you provide has a translation available,
it will be used.
- Buttons. Submit, reset, and regular form button values will
be translated.
- Error messages. Validation error messages will be translated,
with any value substitutions as provided by Zend_Validate.
In other words, translation in Zend_Form is pretty well integrated.
Element Grouping
In Zend_Form, we distinguish between two types of element grouping: grouping
for display purposes (DisplayGroups) and grouping for logistical purposes
(Sub Forms)
With DisplayGroups, you're basically saying you want to visually or
semantically group elements together on the page. Usually (and by default)
this is done with fieldsets. DisplayGroups provide a simple mechanism for
doing this. The elements remain children of the parent form object, but are
rendered within the display group.
Other times, you want to group the elements logically. For instance, you
might want to group a billing address separately from a shipping address.
This grouping may be simple namespacing under array keys (I'll cover this
more later), shared filters or decorators, or, in advanced use cases,
separate pages of a multi-page form.
Zend_Form's answer to these situations are "Sub Forms". They are actually a
subclass of Zend_Form, and the only real difference is the class and the
default decorators used (by default, they render in a fieldset). Since they
share the same functionality as a regular form, this means they can validate
their elements, render themselves, etc. However, Zend_Form itself
cannot iterate over or render a sub forms elements; only the sub
form can do that.
One potentially powerful use case for sub forms is for multi-page forms. You
could easily create a form consisting of several sub forms, and display a
single sub form per page, persisting data in the session between form
submissions; only when all pages have received their data would the parent
form be valid, allowing you to finally pass the data to the model.
Form grouping at the display and logical level both are powerful tools, and
this functionality is trivial with Zend_Form.
Array Support
Many developers like to namespace their form elements under nested arrays.
This allows for groupings of related data, as well as having several groups
with similar data on the same page. As an example, imagine a form showing
both a shipping and a billing address:
<form action="/foo/bar" method="post">
<fieldset>
<legend>Shipping Address</legend>
<dl>
<dt>Address:</dt>
<dd><input name="shipping[address]" type="text" value="" /></dd>
<dt>City:</dt>
<dd><input name="shipping[city]" type="text" value="" /></dd>
<dt>Postal:</dt>
<dd><input name="shipping[postal]" type="text" value="" /></dd>
</dl>
</fieldset>
<fieldset>
<legend>Billing Address</legend>
<dl>
<dt>Address:</dt>
<dd><input name="billing[address]" type="text" value="" /></dd>
<dt>City:</dt>
<dd><input name="billing[city]" type="text" value="" /></dd>
<dt>Postal:</dt>
<dd><input name="billing[postal]" type="text" value="" /></dd>
</dl>
</fieldset>
</form>
PHP will receive two arrays from the submitted form, 'shipping' and
'billing'.
Zend_Form now allows this (as of today). To keep all existing features, and
to allow elements and sub forms to stay de-coupled from their parent forms,
you need to do a little configuration:
$shipping = new Zend_Form_SubForm('shipping');
// This next line tells the elements, validators, and decorators that they are
// part of an array; by default, the sub form name is used:
$shipping->setIsArray(true);
// This can also be accomplished by explicitly setting the array name:
$shipping->setElementsBelongTo('shipping');
The fun part is that this can be arbitrarily deep, by specifying the array
key as it would appear in the form. So, as an example, if we wanted the
entire form returned in the 'demographics' array, and 'shipping' and
'billing' were keys in that array, we could do the following:
// Set base key for entire form:
$form->setElementsBelongTo('demographics');
// Set subkey for shipping sub form:
$shipping->setElementsBelongTo('demographics[shipping]');
// Set subkey for billing sub form:
$billing->setElementsBelongTo('demographics[billing]');
When you set or retrieve values, or validate, these array keys are honored.
What's more, since they are configurable, you can leave them out of your
generic forms, and only set them in your concrete instances -- allowing
re-use and re-purposing.
Conclusion
This post is mainly to serve as high-level overview of some of the more
advanced features of Zend_Form. In the coming weeks, more thorough
documentation will be present in the Zend Framework repository, allowing
developers to understand the functionality in more depth. Hopefully I've
whetted some people's appetites out there, and we'll get more of you
experimenting with the current code base.
Update: fixed array notation HTML example to show separate billing
and shipping addresses.
In a new post to his blog today, Matthew O'Phinney dives ...
Tracked: Feb 11, 10:21