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 Mitchell Hashimoto
Revision 1.0 - 28 August 2007: Initial Proposal
1.1 - 4 September 2007: Changed use cases, modified requirements (wiki revision: 13)

Table of Contents

1. Overview

Zend_Form is a component to simplify the process of form validation, data filtering, and data storage. It will also allow forms to consist of multiple pages and maintain the data across those pages.

2. References

3. Component Requirements, Constraints, and Acceptance Criteria

  • This component will validate form input.
  • This component will filter form input.
  • This component will support Zend_Filter filters.
  • This component will support Zend_Validate validations.
  • This component will support custom filters and validations.
  • This component will allow forms to span multiple pages.
  • This component will manage temporary data storage of multiple pages through storage containers (Zend_Session).
  • This component will allow instances of forms, fields, and pages to be reused within other forms.
  • This component will not provide helpers for modifying the view.

4. Dependencies on Other Framework Components

  • Zend_Exception
  • Zend_Session
  • Zend_Validate_*
  • Zend_Filter_*

5. Theory of Operation

  1. The programmer creates an instance of Zend_Form.
  2. Pages are added to the instance of Zend_Form (optional). If no pages are added, a default page is used for a single-page form.
  3. Fields are added to the Zend_Form_Page instance returned from adding a new page, or directly to Zend_Form if it is a single-page form.
  4. Validators and Filters are added to the Zend_Form_Field instance returned from adding a field.
  5. The data source and other configuration data is set on the Zend_Form instance.
  6. Validation and data filtering occurs in the order they are added. Methods are available to modify the ordering of these actions. Data validation occurs only for the fields of the submitted page, if part of a multi-page form.
  7. If valid and is a single-page form, the programmer saves the filtered data. If it is a multi-page form, the form moves on to the next page, otherwise the programmer saves the data.
  8. If invalid, the user is directed back to the form page and is shown the errors he or she must correct to move on.

The added pages, fields, and validator/filters are added to their own contextual flows. A flow is just a chain of items which occur in a particular order. Pages are seen in order, fields are validating in a particular order, and the validators/filters are run in a particular order. The reason a flow needs to be identified is for branching and reusability. Flows can contain conditionals so that forms may branch to different pages under the circumstance a certain option is set, for example. Additionally, field validation can contain conditionals also, such as certain validations only being run if a condition is met, such as another field being valid. Visual examples of this are available in the use cases.

6. Milestones / Tasks

Describe some intermediate state of this component in terms of design notes, additional material added to this page, and / code. Note any significant dependencies here, such as, "Milestone #3 can not be completed until feature Foo has been added to ZF component XYZ." Milestones will be required for acceptance of future proposals. They are not hard, and many times you will only need to think of the first three below.

  • Milestone 1: Proposal
  • Milestone 2: Implementing single-page form with fields, validators, and filters.
  • Milestone 3: Implementing conditionals to validators/filters.
  • Milestone 4: Implementing multi-page form with Zend_Session temporary storage.
  • Milestone 5: Implementing page conditionals
  • Milestone 6: Unit Tests
  • Milestone 7: Done

7. Class Index

  • Zend_Form
  • Zend_Form_Element (abstract)
  • Zend_Form_Element_Field
  • Zend_Form_Element_Page
  • Zend_Form_Element_Branch
  • Zend_Form_Element_Exception
  • Zend_Form_Flow
  • Zend_Form_Flow_Conditional (abstract)
  • Zend_Form_Flow_Conditional_Valid
  • Zend_Form_Flow_Conditional_Value
  • Zend_Form_Flow_Conditional_Exception
  • Zend_Form_Storage (abstract)
  • Zend_Form_Storage_Session
  • Zend_Form_Storage_Exception
  • Zend_Form_Exception

8. Use Cases

9. Class Skeletons

These are just the skeletons of the main classes. For complete class information, download the package or browse the subversion repository at: http://sourceforge.net/projects/zendform

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

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

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

    <p>I like this proposal much better than other ones. Flows and branches are great ideas. <br />
    It will give more flexibility than other two proposals since it focuses only on validation of received data and transitions between form components. Someone who wants to pass options to select field for example (or any other information to any other field type) could easily derive new class from Zend_Form_Element_Field.<br />
    Keep up good work, I can't wait to see it working.</p>

  2. Aug 31, 2007

    <p>I've created a SourceForge project for this component until it moves up the proposal chain (if it does). I've also finished coding a basic version 0.1 of this component. All the features mentioned above are present and working, and it has a complete test suite done also. There is also a user guide up for instructions on using the component.</p>

    <p>Zend_Form Homepage: <a class="external-link" href="http://mitchellhashimoto.com/zend-form">http://mitchellhashimoto.com/zend-form</a><br />
    SF.net Project Page: <a class="external-link" href="http://sourceforge.net/projects/zendform/">http://sourceforge.net/projects/zendform/</a><br />
    Browse Subversion: <a class="external-link" href="http://zendform.svn.sourceforge.net/viewvc/zendform/">http://zendform.svn.sourceforge.net/viewvc/zendform/</a></p>

    <p>I hope this will get more people interested in this component! I truly believe this is a useful component for most web applications and therefore should be included in the Zend Framework.</p>

  3. Sep 04, 2007

    <p>What about adding some method like Zend_Form::setValidators($validators)and Zend_Form::getValidators()</p>

    <p>Where $validators is an array</p>

    <ac:macro ac:name="code"><ac:parameter ac:name="language">php</ac:parameter><ac:plain-text-body><![CDATA[
    $validators = arrays ('username' => 'Alnum',
    'username' => array('StringLength', 5, 12),
    'password' => 'Alnum'
    );
    ]]></ac:plain-text-body></ac:macro>

    <p>So with Zend_Form::getValidators() you could pass validations rules to a wiew helper responsible for the client side validation.</p>

    <p>And with Zend_Form::setValidators($validators) validations rules could be defined in a config file.</p>

    <p>Best the configuration file could contain validation rules and information about how to layout the form (name, field type, label,... ) which could be handle by a form wiew helper. </p>

    1. Sep 04, 2007

      <p>This is a good idea. A global set validators via array on a page can be very useful, like you said, with config files. I don't quite follow you on the form view helpers since Zend_Form will not provide any view helpers (as is planned).</p>

      <p>Furthermore, it was suggested to me that I add support for filter and validator "namespaces" so that filters and validators other than those in the Zend framework can be used. An example would be:</p>
      <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
      $form = new Zend_Form();
      $form->addValidatorNamespace('Zend_Validate');
      $form->addValidatorNamespace('My_Validate');
      ]]></ac:plain-text-body></ac:macro>
      <p>Although Zend_Validate would be used by default, it just shows how you could add your own namespaces.</p>

      1. Sep 05, 2007

        <blockquote>
        <p>I don't quite follow you on the form view helpers since Zend_Form will not provide any view helpers (as is planned).</p></blockquote>

        <p>I just meant it could be implement.</p>

  4. Sep 05, 2007

    <p>Your solution could be simplified by delegation all the validation and filtering stuff to a Zend_Input_Filter instance. </p>

    <p>Validation rules are pass to Zend_Form as an array, as specified in the Zend_Filter_Input documentation or directly with a Zend_Filter_Input instance.</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $form = new Zend_Form();

    $form->setDataSource($_POST);

    // set filters and validations rules
    $form->setValidationRules(null, $validators);

    // failed
    if ($form->hasInvalid() || $form->hasMissing()) {
    $this->view->errors = $form->getMessages();
    }
    // succeed
    else {
    // handle data
    }
    ]]></ac:plain-text-body></ac:macro>

    1. Sep 07, 2007

      <p>I admit I haven't looked at ZFI in quite awhile, slightly forgetting what features it was composed of. Now that I have reacquainted myself with the ZFI component, I believe that this would probably be the least redundant way of implementing the validation and filter chains. It even has the namespace idea (which I thought was unique). </p>

      <p>The only problem is conditionals with a fields validation and filtering. However, I have an idea which could overcome this, by grouping filters and validators up to a conditional, then having the conditional object, then another ZFI instance below that holding the validators and filters.</p>

      <p>I will look further into this, thank you for the thought.</p>

    2. Sep 09, 2007

      <p>The problem, after looking through ZFI, is that it doesn't support on-the-fly adding of filters or validators. Comments? I'd really like to implement it but one of the key features of Zend_Form is the fact that a flow is run in order, and it seems that with ZFI I can't control this.</p>

      1. Oct 21, 2007

        <p>I see couple of possibilities:<br />
        1. Creating ZFIs on the fly during validation. Each one for one conditional as you mentioned before. Not the most efficient way I presume.<br />
        2. Open ticket for adding filters and validators to ZFI on the fly. This will require implementing some kind of functionality you implemented in the Flow. In this case ZFI would become too similar to your proposal. Also some people might not need this functionality and think that will unnecessarily complicate ZFI.<br />
        3. Create a proposal for a class that will be only able to create filters and validators in the same way as ZFI does. This would simply require some refactoring of ZFI. This way both ZFI and your Zend_Form could proxy to this class methods responsible for creation of filters and validators.</p>

        <p>3rd way in my opinion would be the best solution. I would submit proposal myself if only I had time and signed CLA. </p>

  5. Sep 09, 2007

    <p>I've released a "0.2" packaged release that fixes quite a few problems with 0.1. The biggest issue being the fact that with 0.1, the storage implementation was in place in such a way that with multi-page forms, opening them in multiple tabs (multiple instances of a form), could not be differentiated. Therefore progressing the form in one tab would corrupt the data of the other, and vice versa. I've added a new feature which forces multi-page forms to have a "__uniqueid" hidden field.</p>

    <p>I've also added working demos to the release. As always you can view the latest release notes, API docs, browse SVN, etc. from the main website:</p>

    <p><a class="external-link" href="http://mitchellhashimoto.com/zend-form">http://mitchellhashimoto.com/zend-form</a></p>

    <p>I'm also working on refactoring the nextPage/climbPage methods because they are quite messy. And the test suite needs to be polished (there are currently 108 tests, but its not enough). I've improved the API reference quite a bit but that also still needs some work.</p>

    <p>Any feedback and comments are greatly appreciated.</p>

  6. Oct 21, 2007

    <p>I was playing around with version 0.2 and found one annoying thing.<br />
    Here is what I wanted to do:<br />
    Here is an example of how we can retrieve the data in normal way.</p>
    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    $form = new Zend_form();
    $form->addField('some_field')->setValue('some_value');
    if ($response->isPost()) {
    $form->setDataSource($response->getParams());
    if ($form->isValid())

    Unknown macro: { $data = $form->getData(); //do some stuff with $data }

    }
    ]]></ac:plain-text-body></ac:macro>
    <p>This works fine. But let's say we want to do something with default data set for each field after adding them to a form.</p>
    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    $form = new Zend_form();
    $form->addField('some_field')->setValue('some_default_value');
    if ($response->isPost()) {
    do some stuff
    } else {
    $data = $form->getData();
    //do some stuff with default $data
    }
    ]]></ac:plain-text-body></ac:macro>
    <p>This time $form->getData() throws an exception due to not validated data. When we call $form->isValid() before that line we will get an exception because data source is not set. Ok but in this scenario we don't want to set any other data than this which already sits in each field. Passing empty array as data source won't work because no data will be put into $form->_data and code that tells if form was validated is checking only if $form->_data is null.</p>

    <p>Here are some of my proposition how it could be resolved:<br />
    A parameter could be added to any of methods $form->getData(), $form->isValid(), $form->setDataSource() telling whether or not we want to include default data from fields.<br />
    In my opinion it would be best to tell $form->setDataSource() to use internal data source. Then retrieving data using $form->getData() would still require validation.</p>

  7. Oct 22, 2007

    <p>Another thought. I think it would be useful to allow instances of Zend_Form_Element_Field validate themselves using isValid() method just as Zend_Form allows it. Possibly Zend_Form_Element_Page would benefit from ability of validation as well.<br />
    This way if you would add a 'dirty' flag to each class able to validate you would be able to validate all data only once and not validate everything again when only one field has changed. <br />
    For fields this flag would be true when new value is set or flow has changed and false after validation.<br />
    In pages you would need to listen for changes of the flag in child elements and update its own flag accordingly. It would also need to notify it's parent page about change.</p>

    <p>This way the problem I've mentioned one comment above would be solved as well.</p>

  8. Oct 29, 2007

    <p>A couple more comment </p>

    <p><strong>Validator and Filters</strong></p>

    <p>As Micha? said the validation and filtering could be extract from Zend_Form then it could proxy to this / those class. About validation and filtering Bryce Lohr has done some interesting thing too <a class="external-link" href="http://framework.zend.com/wiki/display/ZFPROP/Zend_Validate_Builder+-+Bryce+Lohr">http://framework.zend.com/wiki/display/ZFPROP/Zend_Validate_Builder+-+Bryce+Lohr</a></p>

    <p>Having filters and validators class coming from a config + an addFields() method would allow some quick form setup, but may be purist won't like it <ac:emoticon ac:name="smile" /></p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $form = new Zend_Form();
    $form->addFields(array('username', 'password', 'firstname', 'lastname')
    $form->addValidators($validators);
    $form->addFilters($validators);
    $form->setDataSource($_POST);
    if ($form->isValid()) {
    $this->_redirect('/success', array('exit' => true));
    }
    ]]></ac:plain-text-body></ac:macro>

    <p><strong>Errors messages and i18n</strong></p>

    <p>It'is a bit of topic, but correct i18n support should be implement in Zend_Validate_* </p>

    <p>1) the '%s' placeholder for the field name in custom error messages is useless for me : the fields name would be 'firstname' but the user will have 'prénom' in front of the form field.<br />
    Bryce has a good solution for custom messages and i18n support.</p>

    <p>2) Most of the time I would be happy with default message but has Zend_Validate_* is not Locale aware, I always have to use custom message which is a pain. </p>

    <p>Well, I guess now we have to wait Matthew's mash up Zend_Form proposal ... Maaattheeeeeeeewwwwwwwwwww !!!!</p>

  9. Dec 04, 2007

    <p>I'm just starting to work with the Zend Framework, and this proposal seems like a valuable, well-designed addition. However, I'm not sure what is the ideal way to display a pre-populated form displaying, for instance, values from a database. </p>

    <p>I'm thinking of situations where a user might need to edit, for example, a calendar event, where the same form would be appropriate for creating new records as well as editing existing ones. Using the Zend_Controller class, I've been creating a single "edit" action, but the logic involved for populating blank/existing/submitted values is rather tedious. Does anyone have a recommendation?</p>

    <p>Thanks much!<br />
    John</p>