UPDATE: This post is now more than 4 ½ years old and the information is woefully out of date. I do not plan to update the code for any newer versions of Zend Framework.
There have been some requests on the Zend Framework mailing lists for information on how to dynamically add elements to Zend_Form. This is something that I’ve been looking into myself, and I’d like to share what I’ve come up with.
Please note that this code is a proof of concept / request for peer review detailing the work that I’ve done to date, and not an example of what I might consider the best way to address this use case. Special thanks go to Cory Wiles who helped me think things through when I first started giving this a go.
First, let’s do a high level walk through of what the code is going to do, then take a look at the code, and wrap up with a live example.
High Level Overview
The form in this example extends Zend_Form and consists of a hidden element that stores an ID, a single text element, buttons for adding and removing dynamic elements, and a submit button. The add and remove buttons are used to trigger a jQuery script that adds and removes dynamic elements. The jQuery script uses the value of the hidden ID element to set element order and make the dynamic element names and IDs unique.
The form class consists of the standard init() method for building the form and two custom methods for dealing with dynamic elements. There is a preValidation() method, called after the form is submitted but before it is validated, that searches the submitted form data for dynamically added fields. If any new fields are found, the addNewField() method takes care of adding the new fields to the form.
jQuery is used to request the new form element from the form’s Controller via Ajax, utilizing the AjaxContext action helper. jQuery is also used to find the most recently added dynamic element, allowing for easy removal of dynamically added elements from the form.
The action controller contains the action that displays the form, and it also has a newfieldAction() that utilizes the AjaxContext to return markup for new fields.
The Zend_Form Subclass
Let’s start with the code for the form. The most important item here is that each form element has its order property set. You can see the huge jump in the order between the “name” element and the “addElement” button. This gap occurs so the dynamic elements can be placed exactly where I want them and so they’ll maintain their position in the form once they’ve been added to the form object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
The action that displays the form is straightforward. If you’ve ever done any work with Zend_Form, I’m sure you recognize what’s going on here. The only thing to note is the $form->preValidation() method. That’s where the magic happens. We’ll get to that in a bit.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
Next comes the controller’s newfieldAction(). This action utilizes the AjaxContext action helper to pass the new field’s markup back to the form view.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
The jQuery script is also fairly straightforward. I attach event listeners to the “Add” and “Remove” buttons that call the ajaxAddField and removeField methods respectively.
The ajaxAddField method makes a post request to the newfieldAction using jQuery’s .ajax method, passing in the current value of the hidden ID element. On success, the new element’s markup is added to the form, and the ID is incremented and stored in the hidden ID element.
The removeField method finds the last element in the page with the class dynamic, removes it, then decrements the current ID and stores the new value in the hidden ID element.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
Zend_Form: preValidation() and addNewField()
Now on to the fun stuff. All of the code up to this point is present to support what happens in the form’s preValidation() method. Remember that preValidation() is called after the form has been submitted but before the form is validated. preValidation() searches through the submitted form’s data for new fields. If it finds any new fields, it calls addNewField() and adds the new fields to the form object. By adding the new form fields to the form object before validation, any filters and validators attached to the new fields will be run as if those fields had always existed in the form object.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
The ability to dynamically add form fields to Zend_Form is a feature I’d really like to see added to Zend_Form. If I were talented enough, I might attempt to make a formal proposal myself. In the meantime, what I’ve come up with can perhaps serve as a starting point for adding very simple elements to very simple forms.
Thanks again to Cory Wiles for helping me work out some of the kinks during the planning phase. Any mistakes, bad practices, or egregious coding errors are the result of my implementation, not his insight and suggestions.
Request for Comments / Peer Review
If you’ve made it this far, I’m grateful to you for hanging in with me. If you have suggestions for improvements to the code, an implementation of your own, or if you see mistakes I’ve made or poor practices that I’ve employed, I’d appreciate your input. Thank you in advance for taking the time to discuss this concept with myself and with the ZF community at large.
Full Controller, Form, and View Code
If you’re interested in the complete code for the controller, form, and views, I’ve posted them over at pastebin. Follow the links below to view / grab the code.
- Action Controller (FormsController)
- Form (Code_Form_Dynamic)
- Form view (dynamic-form-elements.phtml)
- Ajax view (newfield.ajax.phtml)