Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Archived
This proposal has been archived, and deprecated in favor of the consolidated Zend_Form Proposal.

<ac:macro ac:name="unmigrated-inline-wiki-markup"><ac:plain-text-body><![CDATA[

Zend Framework: Zend_Form Component Proposal

Proposed Component Name Zend_Form
Developer Notes http://framework.zend.com/wiki/display/ZFDEV/Zend_Form
Proposers Jurriën Stutterheim
Revision 1.3 - 30 September 2007: Revised. (wiki revision: 21)

Table of Contents

1. Overview

Zend_Form is a component that manages form filtering/validation and, form data/status across multiple pages.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Must provide seperation between form building and form handling.
  • Must provide view helpers to easiliy render form elements or even an entire form.
  • Must provide action helpers to automate form handling
  • Must be able to handle data from multiple pages
  • Must provide a way to store a multi-page form in multiple storage containers (Session, Cache)
  • Could utilize Zend_Filter_Input for form validation and filtering
  • Must not be bound to one output format. Developers must be able to use the Zend_Form for other output than just XHTML.

4. Dependencies on Other Framework Components

  • Zend_Cache_Core*
  • Zend_Controller_Action_Helper_Abstract
  • Zend_Exception
  • Zend_Filter_*
  • Zend_Filter_Input
  • Zend_Session
  • Zend_Validate_*
  • Zend_View

5. Theory of Operation

The basic usage is as follows:
A form page is instantiated. The form processes the submitted data (if available).
If not available, pass the form page to the view and render the form.
If available, validate the form and forward to the appropriate action.
To prevent expired forms there should always be a redirection after the form has been handled.

Zend_Form_Factory

The Zend_Form_Factory class is a factory which makes it easy to generate a Zend_Form. It also makes sure the right datasource is used to populate the form and that the form is serialized in storage.

Zend_Form

The Zend_Form represents one form as displayed on a screen. The form contains a collection of fields (called elements, for cross-format naming) and can be validated as a whole.
The entire form can be serialized into a storage container, so it may be reused in another request. It stores the result of a validation attempt, so the requirements for a form can be checked.
(e.g. shop wizzard example where you don't want to access the payment form before user data is entered)

Zend_Form_Element

A form element contains field-specific filter and validation rules, which will be read from the form page and used for validation.
It can also contain a type-hint, so view helpers can automatically display the proper output format for the field.
There are basicly two different types of form field: Zend_Form_Element and Zend_Form_Element_Set (the upload field is ignored for now).
The former can be used for textfields, textareas, hidden fields etc. The latter can be used for radiobuttons, checkboxes, select lists etc.

Zend_Form_Storage

When a form is validated, the submitted form data and the validation result will be stored for reuse. The Zend_Form_Storage provides a way to do this.
There are two storage methods at the moment: Session and Cache. The latter needs a pre-setup Zend_Cache instance.

View helpers

All Zend_Form_Elements of the form page are accessible in the view. The view helpers make it easy to generate XHTML for your forms.
The default Zend Framework views can be used for this, but there are also a few extra view helpers for Zend_Form.

  • The FormField only needs the Zend_Form_Element instance and decides which view helper to use based on that. In order for this to work a typehint must be set.
  • The MakeForm helper generates an entire form by providing the form page instance. This should only be used for testing/mockup purposes for now. It may be possible to combine this with a Layout or View Enhanced implementation to make the output customizable.
  • The FormElement helper already exists in the ZF today. It has been modified to accept a Zend_Form_Element as argument.
  • FormError reads the validation errors from the form instance and displays them.
  • FormStart generates a <form> tag and a hidden field that contains the unique identifier for that form.

Action helper

  • The FormHandler action helper is a basic skeleton for the workflow described in the operations part. It also has support for form plugins, which will be discussed later.
  • The FormRedirector is a subclass of the default Redirector. It contains a formForward and formRedirect method, which make sure the unique form identifier gets send.

Plugins

The plugins can be registered in the action helper. The plugins make the basic workflow customizable, so form handling can be largly automated. There are three stock plugins:

  • Location: this redirects or forwards the controller to the specified location, based on which button was pressed to submit the form, the status of the form and optionally the status of specific form elements.
  • Modal: checks if the form is modal and if the requirements for it are met.
  • Upload: this handles file uploads.

Builders

The builders make it possible to generate a form instance in an easy way. The most basic builder is the Formname builder. As the name suggests, it takes the name of a form to make an instance. Other builders make it possible to generate a form instance from a Zend_Config instance or a Zend_Db_Table_Abstract instance.

Zend_Form_Filter_Input

As the name suggests this is a subclass of Zend_Filter_Input. It does not require complete filter/validator chains in the constructor and has a few methods to allow dynamic adding and removing of rules.

6. Milestones / Tasks

  • Milestone 1: Make clear what to expect from a Zend_Form component
  • Milestone 2: Define an API
  • Milestone 3: Have a working prototype with examples
  • Milestone 4: Unit tests
  • Milestone 5: Complete documentation
  • Milestone 6: Complete the component

7. Class Index

  • Zend_Form
  • Zend_Form_Builder...
    • _Abstract
    • _Config
    • _Db...
      • _Abstract
      • _Db2
      • _Mysqli
      • _Oracle
      • _Pdo...
        • _Mssql
        • _Mysql
        • _Oci
        • _Pgsql
        • _Sqlite
    • _Exception
    • _Form
    • _Formname
  • Zend_Form_Element
    • Zend_Form_Element_Set
  • Zend_Form_Element_Hint...
    • _HTML
    • _PDF
    • _XForms
  • Zend_Form_Exception
  • Zend_Form_Factory
  • Zend_Form_Plugin...
    • _Abstract
    • _Broker
    • _Location
    • _Modal
    • _Upload
  • Zend_Form_Storage
    • Zend_Form_Storage_Cache
    • Zend_Form_Storage_Abstract
    • Zend_Form_Storage_Session
  • Zend_View_Helper...
    • _FormElement
    • _FormError
    • _FormField
    • _FormStart
    • _MakeForm
  • Zend_Controller_Action_Helper...
    • _FormHandler
    • _FormRedirector

8. Use Cases

Labels:
zend_form zend_form Delete
form form Delete
proposal proposal Delete
multi-page multi-page Delete
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Aug 23, 2007

    <p>At the moment my implementation makes use of Zend_Filter_Input to validate and filter the provided input.<br />
    A lot of people seem to dislike the use of ZFI in _Form and/or in general. The argument I heard most was: it's not API-compatible with _Form.<br />
    The implementation works at the moment and I don't feel like I've added any dirty hacks to make it work with ZFI.<br />
    My questions are: what are concrete arguments against the use of ZFI? What would be a better solution other than: "write something that looks like ZFI and does basicly the same thing, but it's not ZFI"</p>

  2. Aug 26, 2007

    <p>I like the simple use cases as they don't contain any unnecessary "view" information. You don't need any view-like structures in your form models or controllers as everything you need is the incoming data in name and value pairs (whatever the source) and the validation stuff.</p>

    <p>But since the validation is so granularly coupled with the form field instances, I can't see how you are going to validate an XForms data for example. Where all validation rules are passed in a single xml file for all the elements. The same file should be used on client and server side.</p>

    <p>I don't like the Zend_Form_Field_Abstract methods as they imply you are going to inject some rendering attributes to the data containers. You don't know where the data will be coming from - html form, xform, flash, pdf, curl, whatever. So what's the point of tying the models to one visual representation (ie. html)?</p>

    <p>I don't like the "sets" class also - I can't see how it helps in using and validating incoming data. In your business logic the only thing coming in "sets" is an array and everything else comes as a single value. Radio buttons, check boxes, etc, are all html terms and are related to views and not controllers or models. It's only a html representation of a layout. Leave it to the designer and not the developer.</p>

    <p>View helpers are a good way, on the other hand. But you should be able to pass name only instead of getting stuff out of form elements. I.e.:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    Foo: <?php echo $this->formText('username', $attributes = array()); ?>
    ]]></ac:plain-text-body></ac:macro>

    <p>This should be able to access 'username' Zend_Form_Field and get all the data it needs. And remember you pass additional html attributes in views and don't keep it in data models.</p>

    <p>I don't like the naming of the classes - Zend_Form_Field implies it's data is kept in some kind of a visual way. And what if it's comes form a hidden element? Or if the data comes from a command line curl client? Do you still name it that way? As a 'field'?</p>

    1. Sep 01, 2007

      <p>To tell you the truth, I have virtually no experience with XForm handling, so I'm not sure how exactly XForm submitted data will arrive in PHP.<br />
      My guess is that it's available in an array ($_POST/$_GET/other?) like regular form data, so the server-side validation will still work. Any client-side validation will be up to the View part of MVC.<br />
      In case of XForms the use of SimpleXML or DOMDocument seems like a more logical choice to me than Zend_View to build the form.<br />
      Would an action helper (or something like that) which builds the XForm be useful?</p>

      <p>The Zend_Form_Field_Abstract has been removed in the refactoring process. Not much has changed tho. I'm not sure I understand your question. The Zend_Form_Field isn't tied to one visual representation.<br />
      You can assign multiple type hints to a form field, so it can be used with many visual representations.<br />
      If you are refering to the attributes that can be set in the form field, you do have a point that it can be a bit too view-specific. It's something I noticed too, but I haven't made up my mind about it yet.</p>

      <p>The Zend_Form_Field_Set is actually nothing more than a Zend_Form_Field with the ability to store the options for a (to give a HTML example) <selelct> or <input type="checkbox" /><br />
      This is data not tied to a specific visual representation and it allows to supply data to the view from i.e. a database without altering the view much. It also makes a powerfull combo with Zend_Validate_InArray for example. (See also the documentation and examples in the SVN linked at the top of this proposal)</p>

      <p>Any ideas for accessing the form elements are welcome. I'll take a look at the method you described.</p>

      <p>The naming is as always a matter of taste, but I do see your point. A more abstract name could be Zend_Form_Element.</p>

      1. Sep 01, 2007

        <p>The XForms data arrives in the same form as any other form related data - through post or get http methods. Client side validation is done through a XMLSchema, like for example:</p>

        <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
        <!-- payschema.xsd -->
        <xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:my="http://commerce.example.com/payment" targetNamespace="http://commerce.example.com/payment" elementFormDefault="qualified">

        <xsd:element name="payment">
        <xsd:complexType name="payment">
        <xsd:sequence minOccurs="0" maxOccurs="unbounded">
        <xsd:choice>
        <xsd:element ref="my:cc" />
        <xsd:element ref="my:exp" />
        </xsd:choice>
        </xsd:sequence>
        <xsd:attribute name="as" type="my:paymentAs" />
        </xsd:complexType>
        </xsd:element>
        <xsd:element name="cc" type="my:cc" />
        <xsd:element name="exp" type="xsd:gYearMonth" />

        <xsd:simpleType name="cc">
        <xsd:restriction base="xsd:string">
        <xsd:pattern value="\s*((\d+)[-\s])([\d])\s*" />
        </xsd:restriction>
        </xsd:simpleType>

        <xsd:simpleType name="paymentAs">
        <xsd:restriction base="xsd:string">
        <xsd:enumeration value="cash" />
        <xsd:enumeration value="credit" />
        </xsd:restriction>
        </xsd:simpleType>
        </xsd:schema>
        ]]></ac:plain-text-body></ac:macro>

        <p>It's an excerpt from complex xforms example url below.</p>

        <p>It is only natural for me to pick on the schema and validate incoming data with it since it already contains comprehensive validation information. Just take a quick peek:</p>

        <p>Introduction to XForms:
        <a class="external-link" href="http://www.w3.org/TR/2003/REC-xforms-20031014/slice2.html">http://www.w3.org/TR/2003/REC-xforms-20031014/slice2.html</a></p>

        <p>Complete XForms Examples:
        <a class="external-link" href="http://www.w3.org/TR/2003/REC-xforms-20031014/sliceG.html">http://www.w3.org/TR/2003/REC-xforms-20031014/sliceG.html</a></p>

        <p>XForms and XML Validation:
        <a class="external-link" href="http://idealliance.org/proceedings/xtech05/papers/03-07-03/">http://idealliance.org/proceedings/xtech05/papers/03-07-03/</a></p>

        <blockquote>
        <p>If you are refering to the attributes that can be set in the form field, you do have a point that it can be a bit too view-specific.</p></blockquote>

        <p>That's what I have meant. Thank you.</p>

        <blockquote>
        <p>The Zend_Form_Field_Set is actually nothing more than a Zend_Form_Field with the ability to store the options (...)</p></blockquote>

        <p>I still don't get it. If you mean options that can be chosen from a pool of values, then we're talking about validation. I mean it's not important if you have check boxes, select element or a text field to choose newsletters you wish to be subscribed to - you've got two options 'Newsletter A' and 'Newsletter B'. In business logic you have to validate if the incoming data holds the correct values. So, the validators should be holding that data, not the elements.</p>

        <p>And if you mean incoming data then the option will come as a single value or array. So, why can't the Zend_Form_Field hold a value directly? I really don't see the need to subclass this element. Code for demonstration purposes only:</p>

        <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
        $_GET['newsletters'] = array('nl1', 'nl2');
        $field->setValue($_GET['newsletters']);
        ]]></ac:plain-text-body></ac:macro>

        <p>Another thing are element defaults. But those also could be kept in an array form directly.</p>

        <p>You have asked me to take a look into the code examples. I see you keep allowed options in two different places, in the validator as well as in the element, which seems unnecessary at a first glance. And as I have said before - elements should only hold the data (incoming value as well as a default perhaps). The rest lies outside their responsibility in my eyes.</p>

        <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
        /** Color field */
        $form->addField('color', 'Zend_Form_Field_Set')
        ->addValidator(new Zend_Validate_InArray(array_flip($colors)))
        ->setOptions($colors)
        ->addTypeHint(Zend_Form_Field_Hint_HTML::FORMAT, Zend_Form_Field_Hint_HTML::SELECT);
        ]]></ac:plain-text-body></ac:macro>

        1. Sep 01, 2007

          <p>It actually makes perfect sense to me to keep the options in an array in the form field set objects.<br />
          The options are just data and aren't bound to a specific output format. Without this approach the options need to be assigned to the view manually, outside the form object. This prevents automatic form and field generation and to the total time a developer would need to create forms (in coding, debugging and communication with designers).</p>

          <p>Of course this functionality could be moved into Zend_Form_Field, but that wouldn't make sense for "normal" form fields. Although this would make life easier.</p>

          <p>As for XForms, I agree that it would be a shame to have to write validation rules twice. This could be automated in a MakeXForm action helper.</p>

          1. Sep 01, 2007

            <blockquote>
            <p>Of course this functionality could be moved into Zend_Form_Field, but that wouldn't make sense for "normal" form fields. Although this would make life easier.</p></blockquote>

            <p>So is setOptions an optional step? I mean if you don't use a form generator and prepare the form by hand (or just accept form values from some remote form), then you don't need to set that up. Is that true?</p>

            <p>Next thing is - if it's used only in automatic form generation, then maybe it's a good idea to keep the in data there? I mean, in the generator itself.</p>

            1. Sep 01, 2007

              <p>Yes, if you want to manually build the form you could just use a Zend_Form_Field instead of using Zend_Form_Field_Set with setOptions. Not sure how it would respond when given an array as value, but that shouldn't be a problem.</p>

              <p>In case of a XForms generating actionhelper it is possible to store the data in the helper, but for Zend_View helpers it's not possible to do this without some manual extra coding.<br />
              So in short, I don't think the pros weigh up to the cons in this case.</p>

  3. Aug 27, 2007

    <p>I agree with some of Michael's points, but I also see that your implementation is moving very quickly towards a robust solution.</p>

    <p>Could I ask the following Qs:-</p>
    <ul>
    <li>Why do we need a 'Zend_Form_Page' class when 'Zend_Form_Abstract' would suffice? A 'page' may not adequately describe the function of a concrete class, especially if called from a method other than HTTP.</li>
    <li>Could the Zend_Form class extend Zend_Form_Abstract, much as a Zend_View does? The reason I ask is that the only methods in the Zend_Form class are static methods that would still sit comfortably within the overall class structure without conflict. Or perhaps 'get' can be replaced with 'factory' to be more obvious?</li>
    <li>Does the class allow for form names such as 'element<ac:link><ri:page ri:content-title="a" /></ac:link>', 'element<ac:link><ri:page ri:content-title="b" /></ac:link><ac:link><ri:page ri:content-title="c" /></ac:link>' to take advantage of PHP's built-in array handling? I'd like to access a form element by
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$form->

    Unknown macro: {'element[a][b]'}

    ]]></ac:plain-text-body></ac:macro>
    as well as legal element names like
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$form->element]]></ac:plain-text-body></ac:macro></li>
    <li>I agree with the thoughts on 'sets'. It's been PEAR_QuickForm's achilles heel, as sets were never nested and they got in the way when trying to display data in a more complicated way. Validators allow 'sets' of elements to be grouped together anyway.</li>
    <li>I'd see if the existing Form view helpers could be re-worked rather than adding the 'formField' class. How does formError work? Can 'makeForm' accept custom renderers so that developers can finely control the display of forms?</li>
    </ul>

    <p>Saying all this, I really like the simplicity of the controller <-> model relationship and also the controller helpers. It will make it really simple! Can you control the action/controller/module of success/failure/custom events as well?</p>

    <p>I look forward also to Matthew's feedback to get another fresh angle on this. The datasource link looks interesting - is there a use case for this yet?</p>

    1. Sep 01, 2007

      <p>The reason the Zend_Form_Page isn't abstract is because the form must be able to be created in-line in the controller without the need for subclassing. Of course, using a custom subclass to Zend_Form_Page is also supported in the Zend_Form factory.</p>

      <p>When naming the Zend_Form_Page I took a more abstract look at what a form is and came to the conclusion that it's more than just a concrete <form> tag.<br />
      In the checkout process of a webshop the form is an entire order. In this order process the data is collected through multiple concrete <form>s and processed afterwards. Each seperate concrete <form> in this process is a page in the bigger order-form.</p>

      <p>An interesting idea to access form fields that way. What exactly would </p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$form->

      Unknown macro: {'element[a][b]'}

      ]]></ac:plain-text-body></ac:macro>
      <p> do? And how is this more efficient than </p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$form->element]]></ac:plain-text-body></ac:macro>
      <p>?<br />
      Will it provide access to attributes of the element? For example:</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$form->

      Unknown macro: {'name[value]'}

      ]]></ac:plain-text-body></ac:macro>

      <p>Reworking the existing form helpers could be done. It definately would provide a cleaner way to build custom forms. The formField helper still would be a powerfull combo with the makeForm helper.</p>

      <p>The formError helper does nothing more than display the contents of the FlashMessenger. At the moment it's more of a quick-n-dirty way for easy debugging than a serious helper.</p>

      <p>Custom renderers for makeForm are also an interesting idea, but I think I'll wait and see what will happen with Zend_Layout and Zend_View Enhanced first before investigating this.</p>

      1. Sep 01, 2007

        <p>The notation for that form element isn't necessarily an example of efficient access. It's more of a real-world case where you can use PHP's built-in ability to transform appropriately named form elements into multi-dimensional arrays.</p>

        <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$form->

        Unknown macro: {'element[a][b]'}

        ]]></ac:plain-text-body></ac:macro>

        <p>I'd be assuming that the element named in this fashion would create an array from the _GET or _POST parameters, being </p>
        <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[$element['a']['b']]]></ac:plain-text-body></ac:macro>
        <p>. Ideally a Zend_Form object could recognise this notation too and allow this as a legal element name. I used the curly brace notation because you can't physically retrieve that any other way using magic methods with PHP5.</p>

        <p>With regards to the form naming scheme, I'm relucant to call a form a 'page'. To me it's still a form. And a multi-page form is nothing more than a form collection. Yes, I know I'm being picky <ac:emoticon ac:name="smile" /></p>

        <p>Thanks for the clarification on the helpers - good to know that the existing helpers could be utilised in this solution.</p>

        1. Sep 01, 2007

          <p>Ah ok, I didn't get the use of the array notation at first, but I see what you mean now.<br />
          I'll put it on the TODO list right away for investigation.</p>

          <p>As for naming, like I said in the reply to Michael's comment, it's a matter of personal taste. But better naming ideas are always welcome of course.</p>

          <p>To combine Michael's and your naming ideas:</p>

          <p>(current name => renamed)<br />
          Zend_Form => Zend_Form_Factory<br />
          Zend_Form_Page => Zend_Form<br />
          Zend_Form_Field* => Zend_Form_Element*</p>

        2. Sep 01, 2007

          <p>I too am not sure about the word "page" to describe the sub-sections of forms. It has too strong a strong link to the media forms are traditionally displayed on.</p>

          <p>I do think the basic concept behind this implementation is very sound. I especially like the way it tries to avoid being bound to a single medium (like HTML). As far as I am concerned, a form class should in theory allow for forms that get printed on paper and filled in with pencils.</p>

          <p>I really can't wait for a decent form component though. Next to layouts it's the one thing the framework currently is in serious need of, in my humble opinion.</p>

  4. Sep 03, 2007

    <p>Hello,</p>

    <p>We should later add us as solution for working with such third party components (FCKEditor sample here):</p>

    <p><a class="external-link" href="http://wiki.fckeditor.net/Developer%27s_Guide/Integration/PHP">http://wiki.fckeditor.net/Developer%27s_Guide/Integration/PHP</a></p>

    <p>There is already on list Cake and CodeIgniter</p>

    1. Sep 03, 2007

      <p>I'm not really familiar with FCKEditor (largely because it doesn't run under Safari <ac:emoticon ac:name="wink" /><br />
      What would be it's benefits to a form solution?<br />
      As far as I can tell now, it could very well be implemented as a view helper (or added to the current textArea helper), but I'm not sure it is beneficial to a Zend_Form component.</p>

      1. Sep 07, 2007

        <p>FCKEditor, TinyMCE and other rich text editors like the one I'm typing in now allow non-technical people to produce text with markup. While this is often a necessity, I don't think support for them should be a concern for the backend.</p>

        <p>TinyMCE for instance can be activated for textareas that have a specific class-name. This is easy enough to pull of on the view side of a form implementation. The only thing that really does matter is the validation of the html/wiki output of rich text editors, which Zend_Form is able to pull off.</p>

  5. Nov 15, 2007

    <p>I would like to simply see an automatic method for adding nonces to forms. This would help mitigate with accidental double submissions and more importantly mitigate CSRF.</p>