As you might have already noticed from my previous posts, when it comes to form creation I try to avoid coding manual HTML as much as possible. Afterall, we have all that nice elements and decorators available at Zend Framework, so why wasting it?
So, I have one more thing from my Zend Form’s toolset to share with you. At this post I want to create a real table based form with Zend Form. What I mean is this:
A table, but still a form and no custom viewscript. If you want solution right away, you can download the entire code for this post here
Otherwise, see how it is done step by step:
1. Start with a simple form having just a “submit” button:
class Default_Form_SimpleTable extends Zend_Form
{
public function init()
{
$this->setOptions(array(
'elements' => array(
'submit' => array(
'type' => 'submit',
)
),
));
}
}
2. Add table decorators for our main form.
$this->setDecorators(array('FormElements', array('SimpleTable', array('columns' => array('One', 'Two', 'Three'))), 'Form'));
Here I use my custom decorator, what it does is basically adding table/thead/th tags for the form. Titles for the columns can be assigned at “columns” parameter. You can download this decorator with all code for the article here.
3. Define rows.
Each row of our form will be a subform of our main form. Obviously we’ll have several rows in our table, so let’s code creation of such a “row form” in a function in order to be able to call it multiple times later:
public function addRow()
{
$row_form = new Zend_Form(array(
'elements' => array(
'one' => array(
'type' => 'checkbox'
),
'two' => array(
'type' => 'text'
),
'three' => array(
'type' => 'text'
),
),
'decorators' => array('FormElements', array('HtmlTag', array('tag'=>'tr'))),
'elementDecorators' => array('ViewHelper', array('HtmlTag', array('tag'=>'td')))
));
$new_form_index = count($this->_subForms)+1;
$row_form->setElementsBelongTo($new_form_index);
$this->addSubform($row_form, $new_form_index);
return $row_form;
}
So here we:
- define elements which we need.
- assign form decorators – it will wrap the entire row in “<tr></tr>” tags
- assign elements decorators – it will wrap each single element in “<td></td>” tags
- use setElementsBelongTo() in order to add unique prefix to the element name on each row of our form
- finally, add this subform to the main form using $this->addSubform()
4. Add rows to the form.
So now as we have defined our rows, let’s add some to our main form. Let it be 3 rows for this form:
$this->addRow(); $this->addRow(); $this->addRow();
5. Almost done.
Ok, this is it. Form shows nicely as a table. But what’s wrong with our “submit” button? The problem here that all content is wrapped into the table, including our submit button. To make our submit button display nicely we need to add tr/td around it as well. Also we need to make sure it shows under all other rows, we can do it by assigning some large number for “order”:
$this->getElement('submit')->setDecorators(array('ViewHelper', array(array('td'=>'HtmlTag'), array('tag'=>'td', 'colspan'=>3)), array(array('tr'=>'HtmlTag'), array('tag'=>'tr'))));
$this->getElement('submit')->setOrder(100);
6. Enjoy
The good thing is that after submit you will get a nicely structured array of all values like this:
Array
(
[1] => Array
(
[one] => 0
[two] => row1 test
[three] => row1 second test
)
[2] => Array
(
[one] => 1
[two] => row2 test
[three] => row2 second test
)
[3] => Array
(
[one] => 0
[two] => row3 test
[three] => row3 second test
)
)
I used a very similar approach for alot of projects, hope this will be helpful for you also. Combining this idea with some javascript you can also create dynamic forms using Zend Form. Also, each row does not neccessary need to be the same, you can create different set of contorls on each row, just make sure you apply appropriate decorators. So hopefully this was a good starting point for you, which you can use to implement your ideas.
You can download entire code for this article here

Thanks for the post!
I have been using Zend Framework for the past four months and I had trouble taking full advantage of Zend Forms.
That is, the hardest part for me is adjusting how it outputs on the webpage. I need to spend more time reading on decorators.
Your post is really helpful!
Welcome, Francisco!
I’m glad you liked it. I am on my way preparing another post about decorators, thown away few drafts of it already as I wanted to make it as easy to understand as possible.
Share which stuggles do you have. Such kind of feedback will definitely be very useful.
Have been looking for an implementation of something like this for a long time, well done!
Just a quick question though – with the above set up, how would you go about populating the form elements?
Hello Colin,
I’m glad that you found it useful.
When adding a row with $this->addRow(), it returns a subform, you can populate it directly, i.e.:
$row1 = $this->addRow();
$row1->populate($data_for_row1);
$row2 = $this->addRow();
$row2->populate($data_for_row2);
and so on..
Let me know if you have further questions.
You can certainly see your skills in the work you write. The world hopes for even more passionate writers like you who aren’t afraid to say how they believe. Always go after your heart.
thanks for the reply pavel.
Of course! how simple of me.
The reason i ask is because i am looking at displaying a table of data with a single checkbox or radio box element at the end of each row. Only the checkbox and radio are actual elements – the rest of the table row will consist of static data. I was wondering how to display the static data- should they be non-editable/disabled elements? Or styled with css so they don’t look like elements?
I’m not sure which kind of interface you want to create, but it looks like at your specific case it will be easier to use pure HTML. I hate saying so, but I do not see how zend form can be useful in this case
. Your interface looks more like a table, than a form, so it looks like an overkill. Just use checkbox helper directly within your view. Have a look at the small example I’ve create for you here http://paveldubinin.com/?download=Table%20with%20checkboxes.
It is doable with zend form though, e.g. if you look at my post here http://paveldubinin.com/2011/04/7-quick-tips-on-zend-form/ under “2. Custom text or HTML anywhere on the form” you will see how to add such cells. If adding radiobuttons for such table form, you will face another problem which I describe here along with solution http://paveldubinin.com/2011/04/zend-form-radio-buttons/
Hi Pavel
I was able to style them so it looks like a static table with just a checkbox on the end – exactly what i was looking for! I just disabled the other elements and added some css.
Just wondering if i could ask you a couple more questions. The only thing i have left that looks different between this table-form and my normal tables is that the table cells expand themselves to the size of the content they contain, whereas this table-form has cells of the same width. Some cells on my page only display an id, whereas another cell in the same row will display a summary of text, so is there anyway that the width of the cell can be flexible, or adjust to the size of the content?
Also, how do i add a th that has, for instance, a colspan of 3?
Thanks for your help so far
Just check my previous comment. The decorator I describe in this article (SimpleTable) is called simple for the reason.
With it you can create just “default” tables with no colspan or such. You will need to write another decorator if you want something different.
And column width has nothing to do with Zend Form at all. Just check your styles once again.
great example.. but I can’t seem to get
$row1->populate($data_for_row1);
I want to load data from a database into a table (their are child entities).. but can’t
Can you provide an example what $data_for_row1 looks like.
thx
“populate” is just a quick way to set values for multiple elements. So e.g. you could do this:
$form->element1->setValue(‘AAA’);
$form->element2->setValue(‘BBB’);
$form->element3->setValue(‘CCC’);
or just do:
$form->populate(array(
‘element1′ => ‘AAA’,
‘element2′ => ‘BBB’,
‘element3′ => ‘CCC’,
));
Thanks Pavel I have that working now.. My next question is: How do I validate each subform? In my controller I can’t seem to iterate over the subforms and perform an isValid as there doesn’t seem to be any subforms (is this because we are adding subform dynamically?).
Do you have an example of validating data.
thanks.
Did you follow the code from the attachment? At my example forms are created on initialization of the form, so at the moment of form creation you should have all of 3 subforms added and validation of subforms goes automatically.
Also, you don’t need to make any validation at the controller, just update the “addRow” function inside your form by adding necessary validators there.
If your validation logic needs to be somewhat fancy, you might be interested to check this http://paveldubinin.com/2011/06/custom-validation-with-zend-form/
Thanks Pavel, that is the problem..I initiated the form in the controller added one row for each row I find in the database (so I didn’t do it in the init) . I also but a button to add a new row (handled in a controller post action) because i want the user to be able add new data.
I guess I could determine the existing rows in the init() and create appropriate rows and also adding one blank row for new data and therefor. I need to rethink my approach, but I didn’t want to put the data retrieval and validations in the form itself but in the controller logic.
In my opinion validation should be inside a form as it isolates logic specific to the form.
When making a dymamic form, new row needs to be added prior to calling isValid() or as shown at the post I recommended you – by overriding isValid.
So adding rows on init is not a requirement, just make sure you do that before validating.