Skip to end of metadata
Go to start of metadata

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

<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 Matthew Weier O'Phinney
Revision 0.9.0 - 19 December 2007: Initial proposal creation
0.9.1 - 03 January 2008: Minor formatting and grouping changes and corrections
0.9.2 - 03 January 2008: Updates to address community feedback
0.9.3 - 04 January 2008: Add grouping to proposal
0.9.4 - 05 January 2008: Added information on validator/filter enhancements, additional view helpers
0.9.5 - 08 January 2008: Added pluginLoader details to class skeletons
0.9.6 - 09 January 2008: Updated to reflect decorator usage
0.9.7 - 10 January 2008: Updated to reflect current API
1.0.0 - 17 January 2008: Updated with link to initial docs (wiki revision: 26)

Table of Contents

1. Overview

NOTE: Initial documentation is now available

Form processing is a routine and common task for web developers that involves many facets: creation of form HTML, creation of server side validation, and escaping form elements for display on-screen or insertion into data storage; in today's web, we also must consider whether or not a user will be able to interact dynamically with a form element using AJAX.

Considering that web forms are the key to dynamic content on websites, usage of forms should be as easy as possible. Form validation and filtering logic should be encapsulated; if possible, auto-generation of HTML would be desirable.

Zend_Form proposes to do the following things:

  • Form element objects encapsulate the following:
    • Validation chains
    • Filter chains
    • Rendering
      • Decorator classes for elements and forms
      • Default decorator classes for HTML generation
      • Allow multiple decorators
    • Localization hinting (through Zend_View_Helper_Translate)
    • Ability to set element state from a config file
  • Form object encapsulates all form elements
    • Setting current locale in all elements
    • single entry point for validating all elements at once
    • accessors for retrieving individual form elements
    • ability to generate full HTML
      • loops over all form elements and renders them
    • methodology for evaluating AJAX-submitted forms
      • allows validating only passed parameters, not entire form
    • Ability to create all form elements and set form state from a config file

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • Must be de-coupled from Zend_Controller and Zend_View
    • Form setup and validation must be possible without Zend_Controller
    • Should accept data to validate, and not retrieve it automatically from the environment
    • View rendering should be optional, and allow for usage of either Zend_View or custom rendering functionality
  • Must provide separation between validation, filtering, and rendering
    • Must be flexible enough to allow non-XHTML rendering schemas
    • Must have a concept of required/non-required elements
    • Must have methods for:
      • Validation of entire form
      • Validation of partial form
      • Hinting that a request was done via AJAX
  • Must re-use existing components as much as possible
    • Zend_Filter
    • Zend_Json
    • Zend_Locale
    • Zend_Translate
    • Zend_Validate
    • Zend_View_Helper_*
  • Must be able to do both automated and manual rendering of form items
    • Automated rendering:
      • Must be capable of displaying labels, form elements, and error messages
      • Must re-use existing form helpers when possible
      • Must be capable of localization
      • Must be AJAX-friendly
        • Should allow validation of single elements or groups of elements, with a single response
          • Response should default to JSON serialization in a documented format
          • Response format should be configurable via subclassing
        • Should define element IDs
        • Should allow defining arbitrary HTML accessors for use with JS (on* attributes and/or JS-library specific)
        • Could provide a div for error message display
  • Must be able to order form elements
    • Default should be order in which elements are added to form object
  • Must be able to load form and individual form elements from config objects
  • Could be capable of handling multi-page forms (i.e., saves form state between requests)
    • Should provide examples of how multi-page forms could be handled even if no implicit handling is provided.
  • Should allow grouping of form elements for display and/or validation purposes
  • Could allow lazy-loading of validators and/or filters when needed
  • Should either implement validator/filter factories, or require their development
  • Could be extended to allow direct interaction with a model class (such as a Zend_Db_Table)
  • Must provide examples of common Ajax patterns
    • Autocompletion
    • Validation of single/multi elements over AJAX

4. Dependencies on Other Framework Components

  • Zend_Config (optional)
  • Zend_Exception
  • Zend_Filter_*
  • Zend_Json (optional; only used with AJAX)
  • Zend_Locale (optional, through Zend_View_Helper_Translate)
  • Zend_Loader_PluginLoader (for use with factories)
  • Zend_Translate (optional, through Zend_View_Helper_Translate)
  • Zend_Validate_*
  • Zend_View (optional - for rendering and localization)

5. Theory of Operation

Overview

Zend_Form encapsulates any number of Zend_Form_Elements. Each element contains its own validation and filter chains, as well as mechanisms for rendering (be it via view helpers or other mechanisms).

Zend_Form contains metadata about the form itself, including potentially any HTML attributes used to define the form element. It then has methods for validating against all elements or a subset of elements, retrieving errors and error messages (if any), retrieving individual elements, and rendering the form.

Zend_Form_Element will contain metadata about individual elements. Overloading will be provided to allow setting arbitrary metadata, and by default used as HTML element attributes. Additionally, each element will contain its own validator and filter chains, and hinting about how to render itself.

Elements and the form object itself will each have decorators associated with them. By default, elements will use 'ViewHelper', 'Label', 'Errors', and 'HtmlTag' decorators, which will allow using existing Zend_View helpers for rendering the element, label, and errors, and surround the element in an HTML div. Zend_Form_ElementGroup will surround its content using 'ViewHelper' to select the Fieldset view helper, and Zend_Form will use a new Form view helper. Alternate decorators may be specified at any time, and may be nested (first in will be innermost) or chained (with options to prepend or append to content).

Zend_Form and Zend_Form_Element will each have accessors for setting a Zend_Translate object, allowing localization of the form. The various Zend_View_Helper_Form* view helpers will be retrofitted to allow translation, if a translation object is present (most likely by utilizing the proposed Zend_View_Helper_Translate view helper).

Developers will be able to specify Filters and Validators using strings that indicate the full class name and/or the short class name, as well as constructor options. This will enable using Zend_Config to configure a form object. Additionally, validators used with Zend_Form will be passed a second argument to isValid(), $context, which will contain all elements being validated; this will allow validating elements in relation to other submitted values, if required. Finally, all filters and validators will be accessible by name, allowing modification in as well as removal from their respective chains.

Separate proposals for generic AJAX integration with the Zend Framework MVC will be created, including:

Zend_Form_Element

Functionality includes:

  • Zend_Validate validator chain; attach as few or as many validators as needed
    • Allow specifying validators as:
      • objects
      • full class names
      • short class names (minus prefix)
  • Zend_Filter filter chain; attach as few or as many filters as needed
    • Allow specifying filters as:
      • objects
      • full class names
      • short class names (minus prefix)
  • Allows marking element as required/optional (allowing skipping validation if empty or not present)
  • getValue() retrieves filtered value by default
  • getRawValue() for retrieving original provided value
  • Accessor for setting label
  • Accessors for retrieving errors and messages
  • Accessors for setting arbitrary attributes (possibly via overloading)
  • Optionally locale aware
    • if so, all messages and labels will be localized. Configures Zend_View_Helper_Translate with Zend_Translate object provided to element.
  • Method for validating
  • Methods for manipulating decorators for rendering an element
  • Method for rendering
    • Uses any attached decorators
    • __toString() will proxy to render()
  • Method for loading state via a Zend_Config

Typical workflow:

Standard elements to ship with first iteration:

  • Button
  • Checkbox
  • Hidden
  • Image
  • Multiselect
  • Password
  • Radio
  • Reset
  • Select
  • Submit
  • Textarea
  • Text

Standard decorators to ship with first iteration:

  • ViewHelper

Zend_Form

Functionality includes:

  • Accessors for adding, removing, and retrieving Zend_Form_Element objects
    • Adding elements allows optionally setting order
  • Accessors for setting arbitrary attributes (possibly via overloading)
  • Iterable; iterates over attached elements
  • Accessor for setting locale
  • getValues() retrieves filtered element values
  • getValue($name) retrieves single filtered element value
  • getRawValues() retrieves unfiltered element values
  • getRawValue($name) retrieves single unfiltered element value
  • Accessors for retrieving errors and messages for all values or single value
  • Methods for validating:
    • full form (isValid())
    • partial form (isValidPartial())
    • AJAX versions of each of the above
      • Returns standardized JSON response
  • Methods for manipulating decorators for rendering the form
  • Method for rendering
    • Uses associated decorators
    • __toString() proxies to it
  • Method for loading state of form and all elements via a Zend_Config
  • (optional) Ability to group elements into sections/pages

6. Milestones / Tasks

  • Milestone 1: [DONE] Create prototype
  • Milestone 2: [DONE] Create proposal and submit for community review
  • Milestone 3: [DONE] Finalize form element base code and individual elements, including tests
  • Milestone 4: [DONE] Finalize form base code, including tests
  • Milestone 5: [DONE] Test rendering, including JSON responses
  • Milestone 6: Documentation, demos, and tutorials
  • Milestone 7: Component moved to core

If a milestone is already done, begin the description with "[DONE]", like this:

  • Milestone #: [DONE] Unit tests ...

7. Class Index

  • Zend_Form classes:
    • Zend_Form
    • Zend_Form_Decorator_Interface
    • Zend_Form_Decorator_Abstract
    • Zend_Form_Decorator_Errors
    • Zend_Form_Decorator_HtmlTag
    • Zend_Form_Decorator_Label
    • Zend_Form_Decorator_ViewHelper
    • Zend_Form_Decorator_ViewScript
    • Zend_Form_Element
    • Zend_Form_ElementGroup
    • Zend_Form_Element_Exception
    • Zend_Form_Element_Autocomplete
    • Zend_Form_Element_Button
    • Zend_Form_Element_Checkbox
    • Zend_Form_Element_Hidden
    • Zend_Form_Element_Image
    • Zend_Form_Element_Multi
    • Zend_Form_Element_Multiselect
    • Zend_Form_Element_Password
    • Zend_Form_Element_Radio
    • Zend_Form_Element_Reset
    • Zend_Form_Element_Select
    • Zend_Form_Element_Submit
    • Zend_Form_Element_Textarea
    • Zend_Form_Element_Text
    • Zend_Form_Element_Xhtml
    • Zend_Form_Exception
  • Zend_View helpers:
    • Zend_View_Helper_Fieldset
    • Zend_View_Helper_Form
    • Zend_View_Helper_FormErrors

8. Use Cases

9. Class Skeletons

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

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

Labels:
None
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.
  1. Dec 19, 2007

    <p>One thing I liked about Mitchell Hashimoto's proposal was the concept of form flow and especially the ability to have different branches of forms, how difficult would it be to build a similar form from this proposal? </p>

    <p>I think from having a quick look I would need to split the form up into multiple forms?</p>

    1. Dec 30, 2007

      <p>I currently have this marked as a <strong>could</strong> item – i.e., it's something that, if I can find the time to do, will do. In the current incarnation in the laboratory, Zend_Form is completely non-session aware. One thing I'm considering doing is allowing grouping of items for validation – so you could validate one set of fields at a time, and then store them in the session. This shouldn't be too difficult to achive.</p>

  2. Dec 19, 2007

    <p>How do you group elements into fieldsets with legends when rendering? </p>

    <p>Regards,</p>

    <p>Rob...</p>

    1. Dec 20, 2007

      <p>I agree Rob. I think is a much needed feature.</p>

    2. Dec 30, 2007

      <p>There is currently one way to do this: create your own form helper for rendering the form. This of course means you can't auto-render the form (you have to create much of the HTML yourself). I'm looking at adding "group" functionality, but need to investigate how difficult it will be to implement.</p>

  3. Dec 20, 2007

    <p>Hi,</p>

    <p>Nice to see Zend_Form hitting the validation process! <ac:emoticon ac:name="smile" /></p>

    <p>I'm wondering, what you consider as best practises to reuse validation rules. </p>

    <p>What I mean here, is that the validations are associated with the model. And some time you need to use your models in different places of your application. In this use case, for ease of maintenance, I wouldn't want to have to redefined my validation rules each time I use my model for insert or update operations, like this<br />
    $myFormElement->addValidator('NotEmpty') <br />
    ->addValidator('EmailAddress') <br />
    ->addFilter('StringToLower');</p>

    <p>Right now I'm storing my validation rules in the models directory. I'm using an array with Zend_Filter_Input syntax.</p>

    <p>For example for my Users model I have something like that:</p>

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

    + models

    – Users.php
    --+ Users
      – Validators.php
      – Row.php
      – Rowset.php

    // load from module users the users model's validators.
    $validators = Mmx_Loader::getModelValidators('Users_Users');
    $myForm->setValidators($validators);
    ]]></ac:plain-text-body></ac:macro>

    1. Jan 03, 2008

      <p>You can use Zend_Form simply for validating and filtering data; rendering is entirely optional. As such, you could define a form with all its validators and filters, and then use it within your model to validate data passed to it, and filter it for passing into the model storage.</p>

  4. Dec 20, 2007

    <p>If I'm not using Zend_Form to render the form I would like to be able to set all the elements without the need to defined the elements types:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $myForm->setElements(array('username', 'password', 'firstname', 'lastname', 'email'));
    ]]></ac:plain-text-body></ac:macro>

    1. Jan 03, 2008

      <p>I don't see this as being a common use case. However, we could possibly entertain the idea of a "default" element type of 'Text'. I'll take this under consideration, but offer no guarantees that it will make it's way into the final proposal.</p>

  5. Dec 20, 2007

    <p>I'm pretty sure the class in UC-02 was meant to extend Zend_Form, but it currently does not. I'd suggest to add "extends Zend_Form" to UC-02.</p>

    1. Dec 30, 2007

      <p>Fixed – thanks.</p>

  6. Dec 20, 2007

    <p>After reading the other proposals I think this one is the most though out and well crafted one. I feel that it sill needs some work with allows to create groups with field sets and allow you to customize how the fields are rendered by passing a template or something but overall i like it.</p>

    1. Dec 30, 2007

      <p>I'm seeing a number of requests for groups, and will be working on that support. As for how fields are rendered, the idea is to allow passing the name of a view helper to use when rendering the element; this will allow you to customize the output – as well as override the default output by creating a new view helper with the same name but different prefix.</p>

  7. Dec 20, 2007

    <p>Looks good so far. I'm curious, though. How would Zend_Form handle form elements like</p>

    <p><input type="text" name="faveLinks[]"><br />
    <input type="text" name="faveLinks[]"><br />
    <input type="text" name="faveLinks[]"><br />
    <input type="text" name="faveLinks[]"></p>

    <p>or</p>

    <p><input name="AnotherArray<ac:link><ri:page ri:content-title="email" /></ac:link>"><br />
    <input name="AnotherArray<ac:link><ri:page ri:content-title="phone" /></ac:link>"></p>

    <p>or</p>

    <p><input type="text" name="faveLinks[]<ac:link><ri:page ri:content-title="name" /></ac:link>"><br />
    <input type="text" name="faveLinks[]<ac:link><ri:page ri:content-title="url" /></ac:link>"><br />
    <input type="text" name="faveLinks[]<ac:link><ri:page ri:content-title="name" /></ac:link>"><br />
    <input type="text" name="faveLinks[]<ac:link><ri:page ri:content-title="url" /></ac:link>"></p>

    1. Dec 30, 2007

      <p>The second and third cases can be done easily; simply pass the array to the form:</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      if ($form->isValid($_POST['faveLinks'])) { }
      ]]></ac:plain-text-body></ac:macro>
      <p>However, you would need to write your own view helpers for the HTML generation, as the Zend_View form view helpers currently only allow passing scalars, not arrays (though the latter is a scheduled improvement).</p>

  8. Dec 20, 2007

    fc

    <p>Hi Matthew, it's looking good.</p>

    <p>Is there any reason why addElements() was left out? For example:</p>

    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    $elements[] = new Zend_Form_Element_Text('name');
    $elements[] = new Zend_Form_Element_Text('email');
    $elements[] = new Zend_Form_Element_Submit('send', 'Send');

    $this->addElements($elements);
    ]]></ac:plain-text-body></ac:macro>

    1. Dec 20, 2007

      fc

      <p>Sorry, that was a bad example. Here is a real life example, if you are storing your forms in the database for example (can be an array, xml file or something else):</p>

      <p>Database:</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[

      id form_id name class validator filter
      ------------------------------------------------------------------------------------
      1 1 name Zend_Form_Element_Text NotEmpty StringToLower
      2 1 email Zend_Form_Element_Text EmailAddress StringToLower
      3 1 send Zend_Form_Element_Submit EmailAddress StringToLower
      ]]></ac:plain-text-body></ac:macro>

      <p>Example:</p>
      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      $formElements = array();
      foreach ($rs as $element) {
      $obj = $element->class($element->name);
      if ($element->validator !== null)

      Unknown macro: { $obj->addValidator($element->validator); }

      if ($element->filter !== null)

      Unknown macro: { $obj->addFilter($element->filter); }

      $formElements[] = $obj;
      }

      $form = new Zend_Form();
      $form->addElements($formElements);
      ]]></ac:plain-text-body></ac:macro>

      <p>Regards,<br />
      Fed.</p>

      1. Dec 30, 2007

        <p>I'll add this feature to my TODO. <ac:emoticon ac:name="smile" /></p>

  9. Dec 20, 2007

    <p>I am curious about the various ways that the form itself can be rendered. Will there be the ability to alter how the form and the elements themselves are wrapped? There are various methods (via tables, divs, etc) to render a form. In the drafts above I'm not seeing where the various display elements can be adjusted, as well as error messages.</p>

    1. Dec 21, 2007

      <p>Rendering is done with view helpers, so you will be able to write your own.<br />
      You can check the prototype here: <a class="external-link" href="http://framework.zend.com/svn/laboratory/Zend_Form">http://framework.zend.com/svn/laboratory/Zend_Form</a></p>

    2. Dec 21, 2007

      <p>FormCompositeElement view helper: Lab version: <a class="external-link" href="http://framework.zend.com/svn/laboratory/Zend_Form/library/Zend/View/Helper/FormCompositeElement.php">http://framework.zend.com/svn/laboratory/Zend_Form/library/Zend/View/Helper/FormCompositeElement.php</a></p>

  10. Dec 21, 2007

    <p>A few suggestions:<br />
    1.) element grouping for fieldsets<br />
    2.) form wide validation (validating that a password and password_confirm elements are the same). Could be as simple as giving the Zend_Form_Abstract a validator chain.<br />
    3.) It would be nice to be able to state what is considered the "not provided" value for the "requiredness" check. Sometime '0' is considered not provided instead of the empty string.<br />
    4.) For efficiency, it seems like a waist of resources to build all the validator/filter chains upon initial display of a form. A while back I remember a form proposal that built the validator/filter chains only it the form detected input. It's a small hit, but a hit none the less.</p>

    <p>We should make use of the Zend_Filter_Input. This proposal seems to reinvent the wheel and bypass a class that's already built for filtering, validating, and providing error messages.</p>

    1. Dec 30, 2007

      <p>1) Based on user requests, I'll be investigating adding this functionality (element grouping)<br />
      2) I'm considering allowing each element access to all other values, as this would keep them atomic, but allow you to use other values as part of your validation.<br />
      3) empty() will be used for the requiredness check (i.e., !empty() will indicate a value is present)<br />
      4) Any ideas for lazy loading I'd gladly accept</p>

      <p>As for your comment on Zend_Filter_Input, I chose to instead use Zend_Validate for validation chains and Zend_Filter for filter chains as these are what they were designed for. Zend_Filter_Input is not desirable for this component as it is used for <strong>groups</strong> of elements – i.e., it can traverse many elements, but is not designed for traversing a <strong>single</strong> element. We want to keep elements atomic, allowing them full access to their own validators, filters, and renderers.</p>

  11. Dec 21, 2007

    fc

    <p>On your proposal you mentioned that one of the requirements is the ability to load a form and individual form elements from config objects. The component <a href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Di+-+Dependency+Injection+Container">Zend_Di</a> is designed to do that, the idea behind Zend_Di is to load an object based on its dependencies, or in this case, form elements. </p>

    <p>Let me show you an example of how you can build a form using Zend_Di. The example bellow produces the same result as the code showed in Use Case 2.</p>

    <table><tbody>
    <tr>
    <td><p>UC-02</p></td>
    </tr>
    </tbody></table>

    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    $form = new Zend_Form_Builder();
    $form->buildForm(new MyForm());
    ]]></ac:plain-text-body></ac:macro>

    <p>You'll need to add new class, in this case called Zend_Form_Builder:</p>

    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    class Zend_Form_Builder extends Zend_Form_Abstract {
    public function buildForm(Zend_Di_Container $obj);
    }
    ]]></ac:plain-text-body></ac:macro>

    <p>And this is the config file:</p>

    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    class MyForm extends Zend_Di_Container
    {
    protected $_config = array(
    'MyForm' => array(
    'class' => 'Zend_Form',
    'instanceof' => 'Zend_Form_Abstract',
    'arguments' => array(
    'addElement' => 'Element_Name',
    'addElement' => 'Element_Email',
    'addElement' => 'Element_Send',
    ),
    ),
    'Element_Name' => array(
    'class' => 'Zend_Form_Element_Text',
    'instanceof' => 'Zend_Form_Element',
    'arguments' => array(
    '__construct' => 'name',
    'addValidator' => 'Validate_NotEmpty',
    'addFilter' => 'Filter_StringToLower',
    ),
    ),
    'Element_Email' => array(
    'class' => 'Zend_Form_Element_Text',
    'instanceof' => 'Zend_Form_Element',
    'arguments' => array(
    '__construct' => 'email',
    'addValidator' => 'Validate_NotEmpty',
    'addValidator' => 'Validate_EmailAddress',
    'addFilter' => 'Filter_StringToLower',
    ),
    ),
    'Element_Send' => array(
    'class' => 'Zend_Form_Element_Submit',
    'instanceof' => 'Zend_Form_Element',
    'arguments' => array(
    '__construct' => 'send, Send',
    ),
    ),
    'Validate_NotEmpty' => array(
    'class' => 'Zend_Validate_NotEmpty',
    'instanceof' => 'Zend_Validate_Abstract',
    ),
    'Validate_EmailAddress' => array(
    'class' => 'Zend_Validate_NotEmpty',
    'instanceof' => 'Zend_Validate_Abstract',
    ),
    'Filter_StringToLower' => array(
    'class' => 'Zend_Filter_StringToLower',
    'instanceof' => 'Zend_Filter_Interface',
    ),
    );
    }
    ]]></ac:plain-text-body></ac:macro>

    1. Dec 22, 2007

      fc

      <p>The example above sets the configuration by extending the parent class. But with Zend_Di you can also set an instance of Zend_Config, that means that it will convert XML and INI files as well. For that to happen, you'll need to refactor the example I posted above, to something like this:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      class Zend_Form_Builder extends Zend_Form_Abstract {
      // inherits public function setConfig(Zend_Config $config);
      public function setForm(Zend_Di_Container $obj);
      public function buildForm();
      }
      ]]></ac:plain-text-body></ac:macro>

      <p>And to build a form based on a Zend_Config instance:</p>

      <table><tbody>
      <tr>
      <td><p>UC-02</p></td>
      </tr>
      </tbody></table>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      $form = new Zend_Form_Builder();
      $form->setConfig(new Zend_Config($config));
      $form->buildForm();
      ]]></ac:plain-text-body></ac:macro>

  12. Dec 24, 2007

    <p>Hi Matthew,</p>

    <p>Great to finally see your proposal. It answers some questions I had after browsing through the Form lab code.<br />
    Will your Form implementation provide a method to prevent expired forms?</p>

    <p>I look forward to seeing Form hit the incubator. If you're in need of helping hand for Form you can always send me an email <ac:emoticon ac:name="smile" /></p>

    1. Dec 30, 2007

      <p>Jurrien – thanks for looking through the code!</p>

      <p>Right now, no, no methods for preventing expired forms... simply because in the current incarnation, it is session unaware (session awareness is currently left to the developer). However, multi-page forms and grouped items are a common request, so this will likely be addressed.</p>

      <p>I'll definitely drop you a line if I need some helping hands!</p>

  13. Jan 07, 2008

    <p>What about to change the method signature of getValues() and getUnfilteredValues() to</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // If no array is provided as argument the methods will return all the values
    public function getValues(array $names=array());
    public function getUnfilteredValues(array $names=array());
    ]]></ac:plain-text-body></ac:macro>

    <p>I see some value when you can have one form to update 2 or more tables with Zend_Db_Table. In this Use Case you will need to get the data specific to each model. </p>

  14. Jan 09, 2008

    <ac:macro ac:name="note"><ac:parameter ac:name="title">Zend Comments</ac:parameter><ac:rich-text-body>
    <p>This proposal is approved for incubator development, and the following issues are to be addressed:</p>
    <ul>
    <li>Rename <code>processXHR()</code> to <code>processAjax()</code></li>
    <li>Create AJAX use cases for:
    <ul>
    <li>Validating one or more elements</li>
    <li>Autocompletion</li>
    <li>Returning arbitrary responses (this may be part of the ContextSwitch docs)</li>
    </ul>
    </li>
    <li>Element name sanitization
    <ul>
    <li>Either document that only HTML-safe element names should be used,</li>
    <li>Or sanitize them in <code>Zend_Form_Element::setName()</code></li>
    </ul>
    </li>
    <li>Rename <code>Zend_Form_Abstract</code> to <code>Zend_Form</code> and do not make it abstract</li>
    <li>Update the Fieldset and FormFieldset view helper signatures to reflect actual usage</li>
    <li>Consider two types of grouping:
    <ul>
    <li>"Virtual" grouping for display purposes</li>
    <li>Grouping of elements for semantic or logical purposes</li>
    </ul>
    </li>
    <li>Additional decorator types:
    <ul>
    <li>ViewScript, for rendering a view script to display the element/form</li>
    <li>ViewPartial, for rendering a partial view script to display the element/form</li>
    </ul>
    </li>
    </ul>
    </ac:rich-text-body></ac:macro>

  15. Jan 14, 2008

    <p>How would I achieve the following output?</p>

    <p><p class='error'>Some error</p><br />
    <label for='myelement'>Element Label</label><br />
    <input type='text' name='myelement' class='inputText' id='myelement'/></p>

    <p>I didn't see a way of rendering the error, label, and element individually in the view. Is this possible?</p>

    <p>Also, is there a way to test if an element is required or not?</p>

    <p>For example, my html looks like the following for required elements and optional elements.</p>

    <p><div class='required'><br />
    <label for='myelement'>Element Label</label><br />
    <input type='text' name='myelement' class='inputText' id='myelement'/><br />
    </div><br />
    <div class='optional'><br />
    <label for='myelement'>Element Label</label><br />
    <input type='text' name='myelement' class='inputText' id='myelement'/><br />
    </div></p>

  16. Jan 16, 2008

    <p>I like the Zend_Form component, but I have a few suggestions, how to make it better:</p>

    <p>a) Input should have some value, which could be set as default. e.g. in input 'email' is sometimes kept '@'. When the email is forced and user submits the form, the return message should be 'E-mail input was not filled in.' Instead of "Email '@' is not valid."</p>

    <p>b) Global settings for forms will be good. I will be glad, if I can set up in bootstrapto apply StringTrim filter to all inputs.</p>

    <p>c) Some inputs should be blanked after form submit in case of error. Typically password input.</p>

    <p>d) We need some condition system. I think, that conditions if-has-value, if-has-not-value, if-empty, if-not-empty will be enough. It's important when user chooses one option and according to this decision are rules applied to other input.</p>

    <p>e) Multi-input creation should be possible. They can be created by javascript and it should be possible to have rules for them. Typically in the eshop app. Administrator can send photos to every product a every photo has an input for title and description. I want to set maximal amount of photos to 15 and have all titles required.</p>

    1. Jan 16, 2008

      <p>Regarding (a), this is dependent on the validator used.</p>

      <p>Regarding (b), please explain more. Do you mean specifying a filter once to use with all elements?</p>

      <p>Regarding (c), this can be achieved with decorators and/or custom view helpers. I'll happily review proposals for any such classes.</p>

      <p>Regarding (d), please elaborate. </p>

      <p>Regarding (e), you can do grouped elements now via subForms, which are logical groupings of elements.</p>

    2. Jan 20, 2008

      <p>I agree on c). If the browser is going to interpret the input as type="password" then it is going to mask the users input. It seems inconsistent to me to then openly display the user's private input in an error message. Sure the displayed input will be a typo but say someone's looking over your shoulder and sees "greemyard14" in the error message? It might not be that hard to guess "greenyard14" as the user's real password going by the non-word "greem" and the fact that "m" is next to "n" on the keyboard.</p>

      <p>If it is a concern, it appears to me that the problem is in the validators being used for the password rather than Zend_Form as they supply the formatted error message. So I guess the solution is to use setMessages() on each validator that's going to be run against the password?</p>

  17. Jan 20, 2008

    <p>I need to include user help for each form input and also other things like a date picker for instance. I want to keep it all self contained so I can still simply render the form.</p>

    <p>I don't see how Zend_Form_Element lets you do this. If say "userHelp" is passed as an option it gets treated as an html attribute when it is really just custom defined data to be made available to custom decorators.</p>

    <p>Perhaps a customElementData property could be added with get/set methods so that I might store and access an array of this kind of stuff without interfering with the element classes?</p>

    1. Jan 21, 2008

      <p>I imagine that the help is a description for each field in the Drupal CMS style. That can be easily achieved extending Zend_Form_Element adding protected $_description property + public getter & setter.<br />
      Next step is to create a custom decorator (say Description) wich during the rendering retrieves $_description property ($element->detDescription) and appends (or prepends) it to the output.<br />
      One thing to keep in mind is that the order of Decorators is important so you will need to add Description until the HtmlTag decorator.</p>

      <p>The datepicker isn't very hard to code too. Here is my formData helper class. It uses jQuery and jsCalendar but it's only a preference. You could use any other libraries. It is my first attempt to extend a class, so do not be too strict with me.</p>
      <ac:macro ac:name="noformat"><ac:plain-text-body><![CDATA[
      class My_View_Helper_FormDate extends Zend_View_Helper_FormElement
      {
          protected $_lineSeparator = PHP_EOL;

          public function formDate($name, $value = null, $attribs = null)
          {
              // Get name, value, attribs, options, disable
              $info = $this->_getInfo($name, $value, $attribs);
              extract($info);

              // Add "jscalendar" class
              if (array_key_exists('class', $attribs) && !empty($attribs['class']))

      Unknown macro: {            $attribs['class'] .= ' jscalendar';        }

      else

      Unknown macro: {            $attribs['class'] = 'jscalendar';        }

              // Less typing
              $baseURL = Zend_Controller_Front::getInstance()->getBaseUrl();
              $view = $this->view;

              // Add necessary Javascripts to headScript stack
              $view->headScript()->offsetSetFile(0, $baseURL . '/scripts/jquery-1.2.1.js');
              $view->headScript()->offsetSetFile(1, $baseURL . '/scripts/amep.js');
              $view->headScript()->offsetSetFile(2, $baseURL . '/scripts/jscalendar/calendar_stripped.js');
              $view->headScript()->offsetSetFile(3, $baseURL . '/scripts/jscalendar/calendar-setup_stripped.js');
              $view->headScript()->offsetSetFile(4, $baseURL . '/scripts/jscalendar/lang/calendar-es.js');
              $view->headScript()->offsetSetFile(5, $baseURL . '/scripts/calendar.js');

              // Add necessary CSS files to headLink stack
              $view->headLink()->offsetSetStylesheet(0, array(
                  'type' => 'text/css',
                  'rel' => 'stylesheet',
                  'media' => 'all',
                  'href' => $baseURL . '/scripts/jscalendar/skins/aqua/theme.css',
              ));
              $view->headLink()->offsetSetStylesheet(1, array(
                  'type' => 'text/css',
                  'rel' => 'stylesheet',
                  'media' => 'all',
                  'href' => $baseURL . '/styles/calendar.css',
              ));

              // Build the element
              if ($disable)

      Unknown macro: {            // disabled            $xhtml = $this->_hidden($name, $value)                   . $this->view->escape($value);        }

      else {
                  // enabled
                  if (isset($attribs['dateFormat']))

      Unknown macro: {                $dateFormat = $attribs['dateFormat'];            }

      else

      Unknown macro: {                $dateFormat = '%Y-%m-%d';            }

                  $xhtml = '<input type="text"'
                      . ' name="' . $view->escape($name) . '"'
                      . ' id="' . $view->escape($id) . '"'
                      . ' value="' . $view->escape($value) . '"'
                      . $this->_htmlAttribs($attribs)
                      . ' />'
                      . $this->_lineSeparator
                      . $this->_hidden(
                          $view->escape($name) . '-jscalendar-ifFormat',
                          $view->escape($dateFormat),
                          array('id' => $view->escape($id . '-jscalendar-ifFormat')));
              }

              return $xhtml;
          }
      }
      ]]></ac:plain-text-body></ac:macro>

      1. Jan 21, 2008

        <p>I guess I've got to do a lot of sub classing then, but it looks like a tidy way to go. Thanks Karen.</p>

        1. Jan 22, 2008

          <p>The other way is create only custom decorator and not extend the element. If I am not mistaken, in that case the description can be set as attribute trought options of the element and retrieved with getAttrib<ac:link><ri:page ri:content-title="'description'" /></ac:link> from within the decorator.<br />
          Again, if I'm not mistaken, the disadvantage is that you must reorganize decorators (I mean cleanDecorators + addDecorators) for each element that requires description.</p>

  18. Jan 21, 2008

    <p>Hi. Is it possible to put here some example of DisplayGroup realization. Only some outlines I mean :-D</p>

  19. Jan 22, 2008

    <p>Hello Matthew!</p>

    <p>While testing 'Putting It Together' example from Initial Documentation, I note that elements that have been submitted with empty values are validated anyway despite the 'required' flag setted to false.</p>

    <p>I think this is not the expected behaviour. Suppose the case you have optional secondary email text field named 'secondary_mail' in the form. It must be valid email address but only when filled in by the user. When the user submits the form without the 'secondary_mail' filled in, the key is present in the $_POST array but it is empty, so the $form->isValid() fails.</p>

    <p>As can I see, the default state for the element is 'not required', so the the line 1148 in Form.php (rev. 7563) could be changed with something like:</p>
    <ac:macro ac:name="noformat"><ac:plain-text-body><![CDATA[
    change:
    1148 $valid = $element->isValid($data[$key], $data) && $valid;

    to:
    1148 if ($element->getRequired() || !empty($data[$key]))

    Unknown macro: {1149 $valid = $element->isValid($data[$key], $data) && $valid;1150 }

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

    <p>Anyway it is a great component wich saves a lot of time of writing and I'd love to see it in the core as soon as possible. So I vote it with my both hands <ac:emoticon ac:name="smile" />.</p>

    1. Jan 24, 2008

      <p>Hi! I can't figure out how to pass the validation without errors on non required elements with empty value and attached validators :-S</p>

    2. Feb 07, 2008

      <p>I second this, for me this was confusing behavior as well.</p>

    3. Feb 13, 2008

      <p>Because the variables are set dispite beeing empty...<br />
      in Zend/Form.php - function isValid(array $data)</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      change:
      1290            if (!isset($data[$key])) { 
       to:
      1290             if (empty($data[$key])) { 
      ]]></ac:plain-text-body></ac:macro>

  20. Feb 04, 2008

    <p>Hi!</p>

    <p>Sorry for my "english" </p>

    <p>One problem with XHTML manual. Container <label>, attributte "for" is for id of element not for name of element.</p>

    <p>Is it:</p>
    <ac:macro ac:name="code" />
    <ac:macro ac:name="noformat"><ac:plain-text-body><![CDATA[
    <label for="q">Value</label>

    <input type="text" name="q" id="wtfq" value="" />
    ]]></ac:plain-text-body></ac:macro>
    <ac:macro ac:name="code" />
    <p>Should be:</p>
    <ac:macro ac:name="noformat"><ac:plain-text-body><![CDATA[
    <label for="wtfq">Value</label>

    <input type="text" name="q" id="wtfq" value="" />
    ]]></ac:plain-text-body></ac:macro>
    <p>Problem is in class Zend_Form_Decorator_Label line 84, is it</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    if (!empty($label))

    Unknown macro: {            $label     = $view->formLabel($element->getName(), $label, $options);        }

     
    ]]></ac:plain-text-body></ac:macro>
    <p>Should be:<br />
    []</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    if (!empty($label))

    Unknown macro: {            $label     = $view->formLabel($element->id, $label, $options);        }

    ]]></ac:plain-text-body></ac:macro>
    <p>or method of $element returned ID or name, when ID is null. </p>

    <p>On the site can be more elements with the same name but don't be elements with the same ID attrib.</p>

  21. Feb 15, 2008

    <p>When using Zend_Form_Element_Checkbox I experience some "problems". If we take in mind that we give the element a value ($checkbox->setValue(1)<ac:emoticon ac:name="wink" /> and on filling out the form we don't check this checkbox this value will get passed in the cleanData array when validation succeeded. </p>

    <p>I would expect that you can set the checkbox value and only if the checkbox has been checked the value from this element will be available in the data array</p>

  22. Oct 22, 2008

    <p>At this moment Zend_Form isValid method check for each element if it is valid, passing element value and context values.<br />
    Furthermore the isValid methot of the element set element value and get it filtered (the validation came in place after filtering).</p>

    <p>If you want to implement let's say a chain validator and you need to check the filtered value of an element against the value (filtered) of other element you need to pass the form to the validator. You cannot pass the form to the validator if the form is created form a config file.</p>

    <p>This is why i think that the form isValid method need to populate the form with values first, at least merge context data with the filtered ones, and pass to element isValid methot filtered values for all form elements.</p>