views:

3051

answers:

8

I am currently working on a pretty large application which contains a lot of forms.

Up to this moment, I have always been writing my forms by hand and writing my own validation logic, but I have decided it was about time I started using Zend_Form and it's built-in validation routines.

However, I keep stumbling upon more and more problems concerning the (lack of) flexibility caused Zend_Form_Decorator. Simple tasks like adding an extra button to a single input-element become incredibly difficult tasks.

I have now reached a point where I am seriously considering dropping the Zend_Form_Element + Zend_Form_Decorator approach entirely, but I do not want to lose the excellent validation options.

Basically, I want the best of both worlds:

  • Write forms the way the end-user sees them: in plain HTML
  • Easily add server-side validations to form fields without breaking too much of the ZF standard behaviour

A possible solution I am considering is writing the forms both on the server side as in the views. This would allow me to easily validate my own forms, but the (in my eyes quite big) downside is that each form should be defined twice, which just feels plain wrong.

Are there any guidelines to do this? Have any of you experienced the same, and if so, how have you solved these issues?

I'd very much like to hear your points of view.

+3  A: 

I have been using as many Zend components as possible over the past 10 months, on a large project, and Zend_Form has been the biggest pain in the ***. The forms are slow to render, and hard to make pretty. Don't even get me started on Sub-Forms. I saw an interesting article called "scaling zend_form" but it didn't seem to help much with render speed :(

I am thinking about making all my forms using straight HTML in the view, and only using Zend_Form to do the validation and filtering (not rendering). Either that, OR I will just use Zend_Validate and Zend_Filter, without the Form aspect at all.

A tool is only a tool if it helps you. Otherwise, it's just a hindrance.

lo_fye
This is exactly my experience. Thanks for confirming it is probably not just my stupidity that makes this a pain to work with :)
Aron Rotteveel
+2  A: 

I have been using Zend Framework for about a year and I have only used Zend_Form for one of my projects (the first one). I gave up on Zend_Form after I spent all of 15 mins trying to position my 'or Cancel' link. I do love the integration though.

I now use plain HTML forms and use Zend_Filter_Input in the model (Zend_Db_Table in most cases but I had to add a Service Layer in my last project).

An example snippet of controller code using ZFI in the model. Error handling and common validation methods are in a subclass of Zend_Db_Table and my classes extend it.

A view helper formats the error messages array.

if ($this->_request->isPost()) {
    $data = $this->_request->getPost();
    $event = new Default_Model_DbTable_Event();   
    $event->createRow($data)->save();

    if ($event->hasErrors()) {
        $this->view->errors = $event->getErrorMessages();
        $this->view->event = $data;
    } else {
        $this->_redirect('events');
    }
}
Ekerete
Thanks for your answer. Could you eleborate on how you validate your forms and show the error messages to the client?
Aron Rotteveel
I create an instance of ZFI in the model and also have a property to hold the errors if validation fails. I will post a snippet of controller code below.
Ekerete
I have merged my two posts so my code snippet's above. Will delete the duplicate entry.
Ekerete
+12  A: 

I too find the default decorators to be a massive pain. I understand why they are the way they are, but I think the 'inconvenience factor' has been grossly underestimated.

Anyway, I would recommend using ViewScripts for your forms. Note that these are not the same as Views -- instead, ViewScripts are referenced explicitly in your Form class, act as a "sub-view" of sorts and allow you to control the layout of each and every element. Examples how to use ViewScripts have been somewhat hard to find in the past, but I'll try to take a stab at providing something useful.

First, override loadDefaultDecorators in your form class:

public function loadDefaultDecorators() {
     $this->setDecorators(
         array(
             array('ViewScript', 
                 array('viewScript' => 'foo/bar.phtml')
             )
          )
      );        
}

This will reference a ViewScript named bar.phtml located in /views/scripts/foo. Note the case-sensitive differences in "ViewScript" and "viewScript" above.

Next, you'll have to tweak the decorators applied to each element to ensure it displays but without the annoying dt/dd wrappers. For instance:

$baz = new Zend_Form_Element_Text('bazInput');
$baz->setDecorators(array('ViewHelper','Errors'));

Finally, you'll need to build your ViewScript, such as:

<form method="post" action="<?php echo $this-element->getAction() ?>">
    <table>
        <tr>
            <td><label for="bazInput">Baz:</label></td>
            <td><?php echo $this->element->bazInput ?></td>
        </tr>
    </table>
    <input type="submit" value="Submit Form" />
</form>

Obviously this is a very simple example, but it illustrates how to reference the form elements and form action.

Then in your View, simply reference and output your form as usual. This way you can have much finer control over your form layouts -- to include easily adding Javascript.

I believe this approach resolves both your requirements: you can build forms in plain HTML and still take advantage of the Zend Form validation mechanism.

Cal Jacobson
Thanks for your answer; although I already knew about viewScripts, this seems to be the best solution that fits all my needs.
Aron Rotteveel
I think there's no ViewHelper "Error". It's called "Errors"...
Silvan Mühlemann
Corrected, thanks.
Cal Jacobson
+2  A: 

If you want validators and filters without the forms: Zend_Filter_Input

22.5. Zend_Filter_Input

Zend_Filter_Input provides a declarative interface to associate multiple filters and validators, apply them to collections of data, and to retrieve input values after they have been processed by the filters and validators. Values are returned in escaped format by default for safe HTML output.

Although - My personal suggestion is to learn to craft custom decorators and view helpers. Zend_Form is very powerful, and I've never had problems trying to position/decorate things. Even when building a complex table of permissions and auto generating columns and rows using jQuery - I found the Zend_Form interfaces a time saver. If you have a specific question about how to approach Decorating something I'll gladly help. Open a new question and comment it here or something....

gnarf
+1  A: 

You can use Zend Form and generate the HTML yourself :) You just have to track changes and heep the form elements same in HTML and ZF :)

Tomáš Fejfar
Keeping track of those changes in two places is what Zend Forms are trying to erradicate! It's worth the learning curve (hopefully, I'm still on it)
Matt
Absolutely agree ! I was just answering the question! :)
Tomáš Fejfar
+3  A: 

Here's what I've learned with Zend_Form:

Let it do it's thing, and it will save you a ton of lines of code in the long run.

The trade off is you end up writing more CSS to get things to display the way you want to. Remember, almost any HTML element can be styled to look like anything. By default, Zend_Form gives you plenty of CSS selectors to get as specific (or broad) as you need to get. I've yet to see a case where I couldn't work the default decorators in to exactly what I wanted them to be.

Granted, I have a big, ugly CSS file, but in my experience, it would probably be a big, ugly CSS file eventually anyways. I take the trade off of not worrying about application specific form coding/validation/filtering/handling/etc but dealing with some specifically styled elements in a CSS file.

Tip if you decide to go this route: make sure you're using a CSS style reset script

Andy Baird
+4  A: 

Here are a few decorators that I use in my projects using Zend Form. These , I believe, are simple enough to understand.

      $stdRowDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td', 'width' => '200')), array('Label', array('escape' => false, 'class' => 'zfFormLabel', 'tag' => 'td')), array(array('row'=>'HtmlTag'), array('tag'=>'tr')));
  $startRowDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td')), array('Label', array('escape' => false, 'tag' => 'td', 'class' => 'zfFormLabel')), array(array('row'=>'HtmlTag'), array('tag'=>'tr', 'openOnly'=>true)));
  $startRowOpenOnlyDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td', 'openOnly'=>true)), array('Label', array('escape' => false, 'class' => 'zfFormLabel', 'tag' => 'td')), array(array('row'=>'HtmlTag'), array('tag'=>'tr', 'openOnly'=>true)));
  $midRowDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td')),array('Label', array('escape' => false, 'class' => 'zfFormLabel', 'tag' => 'td')));
  $midRowCloseOnlyDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td', 'closeOnly'=>'true')),array('Label', array('escape' => false, 'class' => 'zfFormLabel', 'tag' => 'td')));
  $midRowCloseOnlyNoLabelDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td', 'closeOnly'=>'true')));
  $endRowDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td')), array('Label', array('escape' => false, 'class' => 'zfFormLabel', 'tag' => 'td')), array(array('row'=>'HtmlTag'), array('tag'=>'tr', 'closeOnly'=>'true')));
  $endRowCloseOnlyNoLabelDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td', 'closeOnly'=>'true')), array(array('row'=>'HtmlTag'), array('tag'=>'tr', 'closeOnly' => 'true')));
  $buttonEndRowDec = array('ViewHelper', 'Description', 'Errors', array(array('data'=>'HtmlTag'), array('tag' => 'td', 'colspan'=>'2', 'align'=>'center')), array(array('row'=>'HtmlTag'), array('tag'=>'tr', 'closeOnly'=>'true')));
  $buttonDecorators = array('ViewHelper', array(array('data' => 'HtmlTag'), array('tag' => 'td', 'class' => 'element')), array(array('label' => 'HtmlTag'), array('tag' => 'td', 'placement' => 'prepend')), array(array('row' => 'HtmlTag'), array('tag' => 'tr')), );
Dennis Day
+1  A: 

I second what lo_fye mentioned. In my experience Zend Forms are clunky and not well thought out.

The solution I found easiest and most flexible is to create two form files, 1. the form class, which initializes the form elements, then a view script to display the form elements. In my form class I turn off all decorators, minus the actual form element. e.g.: ->removeDecorator('HtmlTag') ->removeDecorator('DtDdWrapper') ->removeDecorator('Label') ->removeDecorator('Errors');

Then at the end of the constructor that initializes the form elements, pass the view script: $this->setDecorators(array(array('ViewScript', array('viewScript' => 'path/to/script_form.phtml'))));

In the view script I format the form exactly how I like, then where the input field (e.g.) would be just display that element: $this->element->form_element_id. And notice I remove error decorations and just grab the error stack and display it how I think it should.

The downside is you would create a view script for every form or build some sort of reusable system yourself, so extra work. But in the end its much easier to design forms to fit within your layout, imo.

mardala