Zend_Form_Element_Multi – Tips and Tricks
Posted on December 4th, 2008 in Development | 8 Comments »
I’m responsible for creating a lot of forms at my day job. It seems that any project I get involved in requires at least one form. The Zend Form component has made my life a lot easier. After putting together more forms than I can count, I’ve picked up a couple of tricks that I’d like to share. Here are some for the Zend_Form_Element_Multi elements.
As noted in the API documentation, Zend_Form_Element_Multi is the base class for multi-option form elements. Its direct descendants are the Zend Form Select, Radio, and MultiCheckbox elements. Adding options to these elements is possible using the addMultiOptions method. Most of what I want to cover is about retrieving, creating, and adding options, with a short detour into validation.
Using array_combine
Sometimes you want the displayed element options to be the same options returned by the form (as opposed to displaying a string while returning an id). Perhaps you’ll be sending the value(s) along in an email or storing them as strings in a database. While you can create an associative array with matching keys and values, the process quickly becomes tedious with an array of any appreciable size. Why not use array_combine to make life easier?
$options = array('Vanilla', 'Chocolate', 'Strawberry', 'Cookies and Cream', 'Chocolate Chip'); $options = array_combine($options, $options);
Using array_merge
array_merge is helpful when you’d like to add an item to your options array that isn’t already a part of the options array. For example, I frequently add a “Please make a selection” option to my select elements. Extending the above example, I might choose to add the new option like this:
$options = array_merge(array('Please select a flavor'), $options);
One word of caution: array_merge will reindex numerically indexed arrays. array_merge should never be used in a situation where the original array needs to be preserved, such as a numerically indexed array of id and value pairs pulled from a database. In those cases, I use the + operator.
// $options is an array of database ids and flavor descriptions $options = array('Select a flavor') + $options;
Retrieving options using Zend_Db
I frequently retrieve options from a database, using the record id as the array’s index and a related string as the array’s value. There are a lot of ways retrieve options using Zend_Db, but my favorite is the fetchPairs method.
The
fetchPairs()method returns data in an array of key-value pairs, as an associative array with a single entry per row. The key of this associative array is taken from the first column returned by the SELECT query. The value is taken from the second column returned by the SELECT query. Any other columns returned by the query are discarded.
Here’s what that might look like.
$select = 'SELECT flavor_id, flavor FROM flavors'; $options = $db->fetchPairs($select);
I especially enjoy using this method with Zend_Db_Table, using custom table class methods to retrieve my options. My table class usually looks like this:
class Flavors extends Zend_Db_Table_Abstract { protected $_name = 'flavors'; public function getFlavorOptions() { $select = $this->select()->from($this, array('flavor_id', 'flavor')); $result = $this->getAdapter()->fetchPairs($select); return $result; } }
Grabbing your options now becomes ridiculously simple.
$flavors = new Flavors(); $flavorOptions = $flavors->getFlavorOptions();
Validation with Zend_Validate_InArray
Zend_Validate_InArray is the default validator for Multi elements, but the InArray validator can be a little tricky to implement properly. Below are the two gotchas that I’ve run into.
Let’s say you’ve got a select element in your form. In order to force the user to select an option, you’ve added a “Please Select” option to the beginning of your options array. If you use the default InArray validation against the full list of options, “Please Select” becomes a valid selection, and you may end up stuck with a lot of bad form submissions. In order to get around this, I make sure to pass the original array of options to the validator, and use array_merge or the + operator to add the “Please select” option before adding the options to the element.
// Create list of flavor options $flavorOptions = array('Vanilla', 'Chocolate', 'Strawberry', 'Cookies and Cream', 'Chocolate Chip'); $flavorOptions = array_combine($flavorOptions, $flavorOptions); // Add "Select a flavor" option $flavorMultiOptions = array_merge(array('Select a flavor'), $flavorOptions); // Add flavor options to flavor select element $form->flavor->addMultiOptions($flavorMultiOptions); // Add validation, validating against original $flavorOptions array $form->flavor->addValidator(new Zend_Validate_InArray($flavorOptions));
The second gotcha has to do with option arrays where the keys and values don’t match. InArray tests the element’s selected value against the values of the options array, but what you really want to do is test the element’s selected value against the keys of the options array. The trick is to use PHP’s array_keys function.
// Get flavor ids and descriptions from database $flavors = new Flavors(); $flavorOptions = $flavors->getFlavorOptions(); // Add "Select a flavor" option, preserving original array with the + operator $flavorMultiOptions = array('Select a flavor') + $flavorOptions; // Add flavor options to flavor select element $form->flavor->addMultiOptions($flavorMultiOptions); // Add validation, validating against array keys of the original $flavorOptions array $form->flavor->addValidator(new Zend_Validate_InArray(array_keys($flavorOptions)));
Do you have any Zend_Form tips or tricks that you’d like to share? Have I made any egregious errors above that need to be corrected? Hit the comments and let me know.








8 Responses
I don’t have a tip, but a question.
I have a couple of apps where I can dynamically add new text or select inputs to the form. These element names have [] so they come into the app as an array. I have been trying to figure out how to get Zend_Form to work with this sort of situation. I was not able to figure out how to make Zend_Form correctly output my initial form elements (coming out of the database). I was also not able to figure out how to make it validate numerous selects and text elements and use “something[]” as the elements’ names.
I don’t know if I’ve explained the situation well enough, but if I have, any ideas?
Hey David,
Thanks for dropping by! I’m about to start working on the same thing myself. I’ll write it up when I get it worked out. If you get it worked out before you see a write-up here, drop me a line.
Wow i never knew you could use the plus operator like that – I must have skipped over that on at php school – thanks
I was having the “Choose” option problem and thats a neat solution.
A question:
I need to do different validations for field A depending on the value of field B and (possibly depending on a variable that is not in the form at all – C ).
in this kind of logic:
if (B ==1)
{
validator_B(A);
}
elseif (C)
{
validator_C(A);
}
else
{
validator_Default(A);
}
I understand that validators get a secondary argument called $context – which can be used to check values of other fields, but how can a validator get knowledge of other variables in the environment.
Perhaps this stinks of decoupling gone off – so i may be barking up the wrong tree.
The callback validator looks promising.
I’m not sure if Zend_Form should even be used for these kind of tasks.
Any tips?
Hi jeremy,
Re: Do you have any Zend_Form tips or tricks that you’d like to share?
I have only been using Zend Form for a couple of days – so forgive me if this is a dud tip. I found it useful anyway.
—
The Tip:
When developing, sometimes i just want to play around with my control code and toggle field names, control actions etc.
I just wanted to quickly pass all the variables around from page to page while i experiment.
—
A Quick way of generating a form that simply renders the SERVER Request Variables.
Create a subclass of Zend_Form interface like:
//set up the form
$form_obj = new my_Form_SimpleREQUEST_VARSmimic();
//set up the variables
$form_obj->setupREQUESTVarsAsFields();
//render
$form_obj->render();
—
I will post the code example in a separate comment in case its too long to include here.
and, following on from my previous comment, here’s my example code:
(it was far too long to add as a comment)
http://www.eatmybusiness.com/code_downloads/zend_form_Quick_Form_of_Server_Vars.txt
Hey Ronny,
Thanks for dropping by! Check out my latest post for an answer to your question about conditional validation. I figured it would be easier to post about it than try to include it all here.
Hopefully the post helps more than it hurts
JK
Hello webmaster
I would like to share with you a link to your site
write me here preonrelt@mail.ru
Hi Jeremy.
I’m also working on a form where the user can add new crew members to the form via Ajax and i would be interested on hearing how to sort this thing out, if you or someone get this solved. I haven’t started this bit yet, but it will be coming in a couple of weeks time.
I got a multiselect populated ok (which is an array type), but unfortunately did’t get populated text fields with name attributes like director[] distributor[]. The elements have been stated in my form with ‘isArray’ => true.
Been thinking of bypassing the problem by using the multiselects and modifying the UI with Javascript to get the desired look and feel, if no solution is found.