Zend_Form, the latest and greatest addition to the Zend Framework in version 1.5, is an infusion of the best bits of Zend_Filter_Input, and the Zend_View_Helper system. At first glance it looks like the ideal system for setting up from the simplest to the most complex forms, and this is how most people will see it.
I recently delved into the use of it, and was instantly shocked at a fatal flaw in its design. Where am I supposed to put it?
I try to code in as best practices as I can, and so even when I’m not using the Zend MVC implementation, I’m still trying to keep to MVC style principles. Essentially I always try to separate control logic from view layout.
Zend_Form is about as bad as it gets at separating this. Initialisation involves specifying Zend_Form_Element’s which directly map to their associated Zend_View_Helper’s.
// controller
$form = new Zend_Form();
$form
->setAction('/submit')
->setMethod('post')
->addElement('text', 'user-name', array(
'required' => true,
'default' => 'Fred',
'validators' => array(
array('stringLength', 1, 20),
)
))
->addElement('textarea', 'user-comment', array(
'required' => true,
'validators' => array(
array('stringLength', 1, 255),
)
))
->addElement('submit', 'action', array(
'label' => 'Submit',
));
if ($_POST['action']) {
if ($form->isValid()) {
// insert comment
}
}
// view
echo $form->render();
This snipett of code will create a simple form with a textbox and textarea in. If submitted, the form will be validated, and the comment will be added if it passes.
The assumption is that you put the form initialisation in the controller, as that is where validation and database insertation should be performed. However, that means the controller has to define the HTML tag type of each element. e.g. in this example, that user-name is an <input type=”text” />, and user-comment is a <textarea></textarea>.
This view layout information, not control logic. The only place this should be put is in the view script, however the above is the only way, hence this control is not suited to MVC practices.
Now considering the Zend Framework trys to keep to MVC principles, why is it that Zend_Form completely disregards this? I would assume that this may have been due to lack of communication between teams.
My first thought was, how can this be done better MVC style? The trouble is it may be possible that a display element would be complex, and that it needs control logic associated with it. An element may be a scalar, or contain multiple predefined options.
So, this could be represented by declaring elements as primitive types, such as:
- string
- integer
- enum
- set
There could also be more complex types which define a bit more control behaviour:
- date
These types have several obvious HTML element mappings, e.g. strings map to <input type=”text” /> or <textarea></textarea>, enums map to <select></select> or multiple <input type=”radio” />. A developer doesn’t always need to care about enforcing a specific type, so the Form control could choose for itself.
An example of this in use would be:
// controller
$form = new My_Form();
$form
->setAction('/submit')
->setMethod('post')
->addElement('string', 'user-name', array(
'required' => true,
'validators' => array(
array('stringLength', 1, 20),
))
->addElement('string', 'user-comment', array(
'required' => true,
'validators' => array(
array('stringLength', 1, 255),
));
if ($form->posted()) {
if ($form->isValid()) {
// insert comment
}
}
// view
$form->setElementHelper('string', 'text'); // already set as default
$form->addElement('submit', 'action', array(
'label' => 'Submit',
));
$form->getElement('user-comment')->setHelper('textarea');
echo $form->render();
This would create exactly the same form, but conform to MVC standards.
After facing the same brain issue, you will realize it is actually a part of MVC, only that you must share definition/usage across model and view, i explain in the following:
The definition itself of the Zend_Form (or instantiation) should be a view concern, except validation rules, those go to the domain.
So, define your form class in whatever directory you want (models/controllers/views/forms) but retrieve it from the model.
For example:
you have basic CRUD for User
models/UserModel.php
controllers/UserController.php
views/scripts/create.phtml
views/scripts/read.phtml
views/scripts/update.phtml
views/scripts/delete.phtml
now in UserModel class you should have two methods:
static function getUserCreateForm();
static function getUserUpdateForm();
and UserForm class has two method
function initAsCreate();
function initAsUpdate();
in the controller you would use:
function createAction() {
$form = UserModel::getUserCreateForm();
}
function createAction() {
$form = UserModel::getUserUpdateForm();
}
ideally you should find a way to define/pass validation rules from the model(i hate to call it model, since in my view, it is domain object, model being the entire domain)
this can really happen if you choose not to extend Zend_Form, but to use instances of it right in the static model methods above.
if you think a bit, it makes sens, forms deal with domain data, description of view objects is dependent on this, separation of concern is somehow achieved.
Hope it helped.
Thanks for the response.
I realise that the best choice is to put the code in a model, that way it can be reused, but my problem with it is its validation rules must be declared with the exact html elements which it will get displayed.
Think of it as this, you have two websites, each that have a way of user sending comments, take for example a blog comment, and a twitter-like single line comment. One ideally would be a textarea, and the other an input type=”text”.
You can’t reuse the same controller with a different view in order to achieve this without the controller being aware of this, as the validation rules require pre-defined html elements.
This may be a bad example, as a single line comment would need different validators, but another example, one view wants to display multiple choices as radio buttons, and another a select box. The controller/model when declared in the controller would need to choose which element type to pick.
For this to be truely MVC compatible, the view should be able to manipulate the type of the element, which would need separation of the validation from the rendering element.
Hi,
funny I just posted my first blog-Comment on Matthews Homepage (http://weierophinney.net/matthew/index.php?url=archives/200-Using-Zend_Form-in-Your-Models.html
) from December 2008.
It has not been published yet. I tried to point out:
… ahm, somehow half of the above post got lost. heres the rest …
… point out:
“isn’t this whole “problem” a result of Zend_Form being not SRP? Zend Form does Validation (which is, why one/you should put it in model) _and_ it creates HTML, (which is, why it belongs to view too).
Model should know, how to validate. But it should not care about, if a multi-selection should be displayed as multiple selctbox or a set of checkboxes, neither if a textfield should be marked up with stars (type=passwort), should it?
So a Model (interface) should maybe/ or must pass some model-information to the view, since view must know, that there is e.g. a single-choice-list. But the (View_Helper_Form ???) should then configure, how to display. But then you would need to different form-classes. One for validation only (Model_Form) and one for View. ”
Shortly after I found in the comments of GuestBook.php of Zends Quickstart (9/2009):
* This is the guestbook form. It is in its own directory in the application
* structure because it represents a “composite asset” in your application. By
* “composite”, it is meant that the form encompasses several aspects of the
* application: it handles part of the display logic (view), it also handles
* validation and filtering (controller and model).
Maybe I dont really get the point, why “composite asset” is needed here (with its own directory).
Thx,
Robert