Skip to end of metadata
Go to start of metadata

<ac:macro ac:name="toc" />

<h2>Overview</h2>

<p><code>Zend_Form</code> was a big step forward with the 1.5 release; it provided a number of things that simply worked out of the box:</p>

<ul>
<li>Validation</li>
<li>Filtering against XSS attacks</li>
<li>Form output</li>
</ul>

<p>That said, it came with several problems:</p>

<ul>
<li>Since validation and filtering were integrated into the form, there was no way to push that to the domain model easily; many developers found they were needing to duplicate validation and filtering efforts.
<ul>
<li>This is largely due to the fact that validation/filter chains are attached to individual elements, and not the form itself, making aggregation of all rules difficult.</li>
</ul>
</li>
<li>The decorators, while powerful and flexible, (a) are also difficult to explain to new users, and (b) can pose new difficulties to the end-user (what precedence is taken? how do I alter output of this decorator? why does this one allow wrapping in HTML tags, and this one does not? etc.)</li>
</ul>

<h2>Proposal</h2>

<p>The chief goals of this RFC for refactoring Zend\Form are:</p>

<ul>
<li>Separate validation/normalization from the form object hierarchy; forms should consume validation/normalization chains only, and only for purposes of error reporting.</li>
<li>Move view-related functionality – aka, the decorators – to the view layer, and make the functionality more declarative/programmatic.</li>
<li>Forms, fieldsets, and elements would primarily be value objects. Forms would contain fieldsets and elements, and metadata describing the form. Elements would contain their value, and then metadata describing the element.</li>
</ul>

<h3>General Architecture</h3>

<p>Forms will aggregate:</p>

<ul>
<li>Form metadata (action, method, id, etc.)</li>
<li>Elements and fieldsets (i.e., groups of elements)</li>
<li>Validation/Normalization chains (input filters)</li>
</ul>

<p>Additionally, a Factory subcomponent would be provided. The factory will create Forms (and all the objects forms composes). Optionally, it can use a Builder, which will try to automate creation of forms based on annotated models and a provided Form Definition object.</p>

<h3>Validation/Normalization</h3>

<p>It's unusual to validate an element in isolation. As such, validation chains should be attached only to the form.</p>

<ul>
<li><strong>MUST</strong> allow detaching/attaching validation/normalization chains, hereafter titled <strong>"input filters"</strong></li>
<li><strong>MUST</strong> allow defining a tree of elements (i.e., (nested) fieldsets of elements)</li>
<li><strong>MUST</strong> allow retrieving a tree of error messages
<ul>
<li><strong>MUST</strong> allow passing element-specific error messages to that element</li>
</ul>
</li>
<li><strong>MUST</strong> allow partial validations
<ul>
<li><strong>SHOULD</strong> allow indicating which specific elements must be valid</li>
</ul>
</li>
<li><strong>COULD</strong> allow form elements to hint which validators/filters are desired, e.g., via annotations; a directive would then tell the form to use this data when retrieving the input filter.</li>
</ul>

<p>In practice, the input filters would operate similarly to <code>Zend\Filter\Input</code>, though the goal is to provide a more programmatic API as well as a factory for generating the chains. Additionally, the chains for individual elements should be detachable.</p>

<p>The desired outcome is something like this:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$entity = new SomeEntity($data);
if (!$entity->isValid()) {
$form = new SomeForm();
$form->setInputFilter($entity->getInputFilter());
// pass form to view model, etc.
}
]]></ac:plain-text-body></ac:macro>

<p>Alternately, you could bind a model (an object). The form would validate data via its composed input filter, but, on validation, pass the normalized values into the model.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$entity = new SomeEntity();
$form->bindInput($data);
$form->bindModel($entity);
if ($form->isValid()) {
// do something with the entity
$repository->save($entity);
} else {
// redisplay form
}
]]></ac:plain-text-body></ac:macro>

<p>If you're not binding validated values to an object, you can simply retrieve them as an array.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$entity = new SomeEntity();
$form->bindInput($data);
if ($form->isValid()) {
// Grab the values. If not bound to a model, returns an array; otherwise,
// this returns the model object
$values = $form->getData();
} else {
// redisplay form
}
]]></ac:plain-text-body></ac:macro>

<p>To accomplish partial validation, you hint to the form which elements you're interested in prior to validation.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$form->setValidationGroup('username', 'password', 'password_confirmation');
if ($form->isValid()) {
$values = $form->getData(); // contains only 'username', 'password' and 'password_confirmation'
}
// setValidationGroup(Form::VALIDATE_ALL) resets to default behavior
]]></ac:plain-text-body></ac:macro>

<h3>Rendering</h3>

<p>Decorators are an elegant way to render elements. However, they are also very difficult to teach, particularly to developers not familiar with design patterns. (It also doesn't help that the implementation is mis-named; they more accurately follow the <em>Visitor</em> pattern.)</p>

<p>An additional problem in ZF1 is that developers and/or designers often want very fine-grained control of the output, making the decorators a poor fit.</p>

<p>My proposal is to instead create new and/or modify existing form view helpers. These would consume the form and/or element metadata in order to create output; a given object could be passed to several helpers to create compound output when desired. As a simple example:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
<?php echo $this->form($form)->emitStartTag(); // form opening tag ?>

<?php $email = $form->getElement('email'); ?>
<div class="element">
<?php echo $this->formLabel($email) ?>
<?php echo $this->formEmail($email) ?>
<?php echo $this->formErrors($email) ?>
</div>

<input type="submit" name="submit-email" value="Send" />
<?php echo $this->form()->emitEndTag(); // form closing tag ?>
]]></ac:plain-text-body></ac:macro>

<p>A view helper per general form input type will be provided, along with "generic" view helpers that will introspect the type and proxy to the appropriate specific view helper.</p>

<p>"Wait, this looks like a lot of work!" I hear some saying. However, using the lesson from decorators, repitition can be easily addressed: create helpers for common combinations:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace My\Helper;

use Zend\Form\ElementInterface,
Zend\View\Helper\AbstractHelper;

class WrappedElement extends AbstractHelper
{
public function __invoke(ElementInterface $element, $type = 'text', $class = 'element')

Unknown macro: { $view = $this->getView(); $broker = $view->broker(); $input = $broker->load('form_' . $type); $label = $broker->load('form_label'); $errors = $broker->load('form_errors'); return sprintf( "<div class="%s">n%sn%sn%sn</div>", $view->escape($class), $label($element), $input($element), $errors($element) ); }

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

<p>and the above would be used like this within a view script:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$email = $form->getElement('email');
echo $this->wrappedElement($email, 'email');
]]></ac:plain-text-body></ac:macro>

<p>The goal for ZF2 is to provide a basic set of these "combination" helpers that can be used to accomplish basic form markup with minimal effort by developers. For anything more complex or specific, developers will need to write their own helpers and/or turn to third party modules.</p>

<h3>Factory, Builder, and model binding</h3>

<p>The Factory will be used to create form objects, filtersets, elements, and the input filter used by the form based on the configuration provided.</p>

<p>The Builder component would provide a way to generate forms from models automatically. Additionally, Form would provide methods for mapping values to models automatically. These features will benefit rapid application development.</p>

<ul>
<li><strong>MUST</strong> provide a factory for form elements</li>
<li><strong>MUST</strong> provide a factory for filtersets</li>
<li><strong>MUST</strong> provide a factory for forms</li>
<li><strong>MUST</strong> provide a factory for the input filter</li>
<li><strong>MUST</strong> allow binding form data directly to objects (models).</li>
<li><strong>SHOULD</strong> include a form builder service to build forms from annotated objects automatically (type guessing, filters, validators, etc)
<ul>
<li><strong>SHOULD</strong> be extensible to allow module developers to developer additional annotations, e.g., Doctrine ORM\ODM.</li>
<li><strong>SHOULD</strong> provide caching to reduce the performance hit of building forms from annotations/reflection.</li>
</ul>
</li>
</ul>

<h4>Sample class and definition</h4>

<p>The following classes are used examples below:</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
namespace My\Model;

use Zend\Filter,
Zend\Form\Element,
Zend\Validator as Assert;

class User
{
/**

  • @Assert\Length(
    Unknown macro: {"min"}

    )

  • @Filter\StringTrim
    */
    public $username;

/**

  • @Assert\EmailAddress
  • @Filter\StringTrim
  • @Element\Password(
    Unknown macro: {label="My Super Cool Label"}

    )
    */
    public $password;
    }

namespace My\Form\Definition;

use Zend\Form\Builder,
Zend\Form\Definition;

class User
{
public function build(Builder $builder)

Unknown macro: { // Builder}

// Used to uniquely identify the form.
// Typically, this is the id and/or name used when rendering the form.
public function getName()

Unknown macro: { return 'user'; }

// Sets default Zend\Form options (action, method, etc) as well as
// specifying the model class for the definition (or anything else
// required).
public function getOptions()

Unknown macro: { return array('model_class' => 'MyModelUser'); }

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

<h4>Sample usage (create)</h4>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
// Assume posted data contains:
// username: foo
// password: bar
//
// Assume a configured form factory with a configured form builder has been
// injected into the controller.
public function createAction()
{
$request = $this->getRequest();
$form = $this->formFactory->getForm('My\Form\Definition\User');

if ($request->isPost()) {
$userForm->bindInput($request->post());

$user = $userForm->getData(); // My\Model\User
echo $user->username; // foo
echo $user->password; // bar

if ($userForm->isValid())

Unknown macro: { // do stuff }

}

return new ViewModel(array('form' => $userForm));
}
]]></ac:plain-text-body></ac:macro>

<h4>Sample usage (edit)</h4>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
public function editAction()
{
$user = new \My\Model\User;
$user->username = 'foo2';
$user->password = 'bar2';

$request = $this->getRequest();
$form = $this->formFactory->getForm('My\Form\Definition\User');
$form->bindModel($user);

return new ViewModel(array('form' => $userForm));
}
]]></ac:plain-text-body></ac:macro>

<h3>Interfaces</h3>

<p>These are some of the interfaces for the various objects discussed in the proposal. They should be considered incomplete.</p>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface FormInterface extends IteratorAggregate, FieldsetInterface
{
const VALIDATE_ALL = 1;
const VALUES_NORMALIZED = 2;
const VALUES_RAW = 3;
const VALUES_AS_ARRAY = 4;

public function bindInput($data);
public function bindModel(object $model);
public function isValid();
public function getData($flag = FormInterface::VALUES_NORMALIZED);
public function setInputFilter();
public function getInputFilter();
public function setValidationGroup(); // likely proxies to input filter
}
]]></ac:plain-text-body></ac:macro>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface FieldsetInterface extends ElementInterface
{
public function add($elementOrFieldset, $order = null);
public function remove($elementOrFieldset);
public function getElements();
public function getFieldsets();
public function getIterator(); // iterate over elements and filtersets in order
public function setMessages($messages); // hash of element names => messages
public function getMessages($elementName);
}
]]></ac:plain-text-body></ac:macro>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface ElementInterface
{
public function setAttribute($key, $value);
public function getAttribute($optionalKey);
public function setAttributes($arrayyOrTraversable);
public function getAttributes();
}
]]></ac:plain-text-body></ac:macro>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
interface DefinitionInterface
{
public function getName();
public function getOptions();
public function build(Builder $builder);
}
]]></ac:plain-text-body></ac:macro>

<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
// allows listeners so that a listener can intercept types, inject options, etc
interface BuilderInterface extends \Zend\EventManager\EventManagerAware
{
// Map of element types => classes
public function setTypeMap($typeMap);

// Accept definition object or class name
public function getForm($definition);

// Annotations we know about
public function setAnnotations($annotations);

// Add an element to the form being built
public function add($name, $type = null, array $options = array());
}
]]></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. Feb 22, 2012

    <p>Decorators: I have a few comments here actually since I have had to customize the set of decorators (visitors) numerous times. First off; ZF 1 had a root decorator (Xhtml) which if you needed to modify it would cause you to have to copy each and every other decorator in order for it to properly utilize the features needed. This was undesirable behavior.</p>
    <ul>
    <li>An additional requirement <strong>might</strong> be to ensure no hierarchy or to provide the functionality through a loader. (the perfect trait use case)</li>
    </ul>

    <p>Dynamic Form Elements: The hash element made ajax request processing far more difficult; the hash element should be refactored to allow (the browser) to have multiple tabs and have the ability to persist rather than being hard coded to a single session hop. This is important when processing the same type of form (such as accepting a task, etc).</p>

    <p>Form Errors: Error messages were very difficult in ZF1; for instance, if you assigned an error to an element the form would not automatically mark itself having an error. Additionally the form API caused confusion to several developers when attempting to mark a form as an error. Form Errors should leverage the event system.</p>

    <p>Subforms: I believe that this should be declared differently; subforms in ZF1 were used by different people in different ways. The general use case is for multi-page forms. I would like to see this renamed to better describe the functionality. In addition; it would be beneficial from an end-user perspective to assist in the workflow of multi-page forms.</p>

  2. Feb 22, 2012

    <p>+1 for annotations. Zend\Code already provides the foundation for them and my SpiffyForm module has them working already. It would be easy to port/update that code to work for Zend\Form.</p>

    1. Feb 24, 2012

      <p>-1 for annotations</p>

      <p>Too much magic for me.</p>

      1. Mar 20, 2012

        <p>Usage of annotations would only ever be opt-in. Anything you can do with them, you'd be able to do programmatically as well.</p>

  3. Feb 22, 2012

    <p>I am concerned that the changes proposed here will make forms less useful in ZF2. In ZF1 they are at the heart of many controller actions in my experience. Taking validation and rendering away from forms might provide better separation of concerns, but it means we need provide that functionality elsewhere.</p>

    <p>I think it would help to see more usage examples.</p>

    1. Feb 23, 2012

      <p>Are we using annotations anywhere else in ZF2?</p>

      1. Mar 20, 2012

        <p>Annotations are available as hints to the DI compiler now; they are opt-in, however.</p>

    2. Mar 20, 2012

      <p>Well, much of this stuff is already separate in ZF1, and where it's not, should be.</p>

      <ul>
      <li>While we have the decorator system in ZF1, most of the actual work... happens in view helpers. Decorators are basically an additional layer of abstraction that developers need to master.</li>
      <li>Validation and filtering already use Zend_Validate and Zend_Filter – they simply re-create the validation and filter chains in those components to be domain specific.</li>
      </ul>

      <p>While it seemed like a nice idea to have filters/validators per element, in practice it led to a lot of subtle validation and rendering issues. That said, we'll likely have some with the proposed solution as well.</p>

      <p>In terms of usage examples, for most cases, you'll still do this:</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      $form = new SomeForm();
      if (!$form->isValid($someData)) {
      // return an invalid form, and render it!
      }
      $values = $form->getValues();
      // do something with the validated, normalized values
      ]]></ac:plain-text-body></ac:macro>

      <p>Rendering will look quite different, as it will likely be more explicit. However, you should be able to build helpers for rendering your entire form at once fairly easily – but this also has the advantage that you can change one helper to change rendering in many locations.</p>

      <p>The main difference for creating your form <em>objects</em> will be in how you create your validation chain – which should not affect your controller code at all.</p>

  4. Feb 22, 2012

    <p>I like this design must more than the one in ZF1. I also appreciate the switch in language around "Normalization", as that is how I've always preferred to think about "filtering".</p>

    <p>Thinking more about the annotations, I had been thinking about a "context" metadata item which would allow one to <em>declare</em> multiple contexts (i.e. create, update). Instantiating a form object with a context tag would set the appropriate validators and even elements for that context.</p>

    <p>A simple scenario would be a user sign-up form where on initial creation, a password and password confirmation is needed. This form could be re-used for password changes (where only the password and a confirmation is needed) or for profile updates, where no password is required to make changes. Building up a single form object with context switching is much more tidy when you have multiple use cases that vary only slightly.</p>

    <p>That being said, I'd love for someone to point out that there is already a better way and I'm just doing it wrong.</p>

    1. Feb 22, 2012

      <p>I'd prefer to just extend the original form and $this->removeElement('password'). Keeps things simple and IDE friendly.</p>

      1. Feb 25, 2012

        <p>Yes, I agree that this works (I do this in a couple projects); in fact, I generally create an "abstract*" type for each form with ALL the possible elements, then in the extended classes remove the unnecessary elements for that context.</p>

        <p>For example:</p>

        <p>acme\form\schedule\AbstractAppointment<br />
        acme\form\schedule\CreateAppointment (in many cases, this one has nothing to do but extend)<br />
        acme\form\schedule\UpdateAppointment (this one will generally set a couple instance variables, then remove a few elements)</p>

        <p>I like the composability of this method; however, I see no down-side to having something baked-in as well.</p>

        <p>BTW, Guilherme nails this idea down quite well here: <a class="external-link" href="http://zend-framework-community.634137.n4.nabble.com/Forms-RFC-now-up-td4409226.html#a4420536">http://zend-framework-community.634137.n4.nabble.com/Forms-RFC-now-up-td4409226.html#a4420536</a></p>

        1. Feb 27, 2012

          <p>But this feels like: I'm naked and someone wants to see my underwear. Now, what I do is getting dressed and then take off all of my clothes, except for my underwear. </p>

        2. Feb 27, 2012

          <p>It possibly makes sense not to fully initialize the for instance in the abstract class, but to provide methods that create form elements, add filters and validators, and then add these elements in concrete implementations, while retrieving elements from the abstract classes methods. Still, this doesn't solve the problem that you may need different valuations in different contexts. </p>

          <p>But, the form element meta data for different contexts could easily be created from annotations as well, I assume. </p>

    2. Feb 27, 2012

      <p>+1 for contexts!</p>

      <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
      $formManager = $this->getFormManager();

      $signUpForm = $formManager->getForm(
      'User',
      'SignUp'
      );
      $signInForm = $formManager->getForm(
      'User',
      'SignIn'
      );
      ]]></ac:plain-text-body></ac:macro>

  5. Feb 22, 2012

    <p>I'd like to make a remark about PRG (Post-Redirect-Get). In the current ZF1 Zend_Form, the PRG is only possible if you store the input data and validate twice. If a form contains errors (after POST), you might want a redirect to a GET, but keep the error information for the user. It is currently not possible to store the input data and form errors separately, you must validate on GET again to show the error messages.</p>

    <p>While this is "just" an implementation issue, I think PRG is getting more attention and should be considered as a primary use case for Zend\Form.</p>

    1. Feb 22, 2012

      <p><strong>YES yes yes for PRG!</strong></p>

      <p>Actually it's somewhere between controller and view (plugin? rendering strategy?) than it's about the Form "widget", but I'd really like to see it mentioned in the RFC <ac:emoticon ac:name="smile" /></p>

      1. Feb 22, 2012

        <p>The point is rather about the "setter" for error metadata information. The implementation of PRG is up to the developer (framework <em>user</em>) imho. The problem now is you can set element values (<code>Zend_Form::populate()</code>) but not the error information about invalid elements.</p>

    2. Feb 22, 2012

      <p>I already extended Zend_Form to store error messages into a session and reset it after redirect<br />
      <strong>BUT</strong> it was much slower that re-validating the form after redirect because writing/reading much more session data.</p>

      <p>True if you have expansive validations it's better to store the validation result but in must cases the string length and other simple validations will be made and that shouldn't be stored.</p>

    3. Feb 27, 2012

      <p>But why would you want to redirect after a failed POST?</p>

  6. Feb 22, 2012

    <p>Maybe it's better to avoid things like $entity->isValid() in favor of validating an entity against a validator chain or simply put - entity validator. Something like $validator->isValid($entity).</p>

    <p>This may be useful as you might need to validate an entity against multiple validation strategies. Like for example when you have a User entity wich may or may not require a password field to be set (social login/on-site registration).</p>

    <p>This can also be extended with custom user validators to ensure entity is in valid state for some domain operations in service layer.</p>

    <p>Regarding validation of properties it may be not so very unusual. You may want to validate a single field via ajax for quick ui feedback on input.</p>

    <p>That said I pretty much like the part on decorators & form rendering <ac:emoticon ac:name="smile" /></p>

  7. Feb 22, 2012

    <p>This is going to be awesome.</p>

    <p>Validation:</p>

    <p>It would be great to have a more complete example of how you imagine the model validation, controller action, and form to work together. </p>

    <p>A particular issue I have been struggling with finding a consistent way of doing is that some parts of the validation might be more "view bound" for instance validation of the format of a float, and maybe also the transformation from one decimal separator to another. While the validation of the value of the float is done in the model object (or validator associated with the model).</p>

    <p>Rendering:</p>

    <p>I assume it should still be possible to access form elements using a __get, so that the following is possible?</p>

    <ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
    echo $this->formText($form->email);
    ]]></ac:plain-text-body></ac:macro>

    1. Mar 20, 2012

      <p>Not sure about overloading to elements – this caused us a fair bit of grief in the past, due to the fact that overloading was expected to work for each of elements, display groups, and sub forms. I'd prefer an explicit method call, e.g. <code>$this->getElement('email')</code>, as this makes it clear what you expect in return.</p>

  8. Feb 22, 2012

    <p>I think we could use an anonymous function for view.</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    <?php
    $f = new StdClass(); //simule form class
    $f->action = "index.php";
    $f->method = "post";
    function form($form, $fun_call){ //simula view helper
    echo "<form action='$form->action' method='$form->method' >";
    $fun_call();
    echo "</form>";
    }
    form($f, function(){?>
    <intup type="text" />
    <?});
    ]]></ac:plain-text-body></ac:macro>

  9. Feb 22, 2012

    <p>I'm really sad to hear that decorators are to be eliminated <ac:emoticon ac:name="sad" /></p>

    <p>I'd say that the most common practise in our development is to create the decorator stack first, then create the form and don't care about how will they look - they will be awesome <ac:emoticon ac:name="smile" /> </p>

    <p>We used completly remade decorator stack to style our forms. We did that twice. Once when we were switching our table form layout to Zend_Form (in 1.5) and then lately when we switched to form-fieldset-label-element layout. There were about 70 forms or so. They all changed at once, in predictable manner. There were only a few (about 5 or so) exceptions - that used specific decorators - mostly to align something in the table. Imagine switching 70 forms, some of them with many elements, to different layout using the proposed solution. Thats days work. It took us not more than a few hours! Last but not least - it saves tons of HTML code to write and keeps your views clean for actual content. </p>

    <p>Also the proposed behavior can be archived simply with decorators. Just removeDecorators() and then echo $this->form->element or write simmilar view helper as the one proposed. </p>

    <p>Decorators are great. View helpers for echoing certain elements can not match their benefits. </p>

    1. Feb 22, 2012

      <p>Me too <ac:emoticon ac:name="smile" /> Some sort of super-view-helper that allowed this to happen would be nice.</p>

    2. Feb 22, 2012

      <p>There's no reason you can't take the view helper above and expand on it to handle generating an entire form.</p>

      <p>For example,</p>

      <p><?php echo $this->renderFormAsP($form); ?></p>

      <p>If you had 70 forms all rendered using this one view helper then they would be updated automatically anytime it changed. The edge-cases would be identical for those few forms that required something special and would require manual updating.</p>

      <p>Nothing has changed - it's just a different (better, imo) way to get the same result.</p>

    3. Feb 24, 2012

      <p>+1</p>

      <p>I've actually grown to like them, I just dislike when they're preloaded, or loading things unnecessarily.</p>

      <p>removeDecorators() "remove" assumes they have been loaded; should be disableDecorators() and no extra files and no extra overhead should done.</p>

      <p>Almost everything is pre loaded: elements, filters, validators, a lot of files are loaded and a lot of which is not always necessary; if isValid is never called, why load the validators, filters? If you don't render the form why should the decorators be loaded, and configured, etc.</p>

      <p>I think they should be kept, but made much more lightweight.</p>

      <p>Being able to write a global form+elements <em>decorator definition descriptor</em> as one simple array for example. One that is mergable to a default descriptor might be nice. If that makes sense.</p>

      1. Mar 20, 2012

        <p>Actually, you're wrong. <ac:emoticon ac:name="smile" /></p>

        <p>I did a <em>ton</em> of work within Zend_Form to make sure that things were lazy-loaded. While you specify what you want up front for decorators, filters, and validators, if you look under the hood, unless you pass in objects, no objects are instantiated until they are requested. This means that if you only render the form, no validators will be created (filters will be, as they'll be used to filter any default values you provide); if you only validate a form, no decorators are created.</p>

        <p>With the proposed design, if you do not pass your form to the view, no helpers will be instantiated. If you do not validate, it's likely you'll not get an instance of your input filter, either (assuming it can be lazy-loaded). </p>

  10. Feb 22, 2012

    <p>I added some information regarding object binding and generating forms from annotations similar to <a class="external-link" href="http://www.github.com/SpiffyJr/SpiffyForm">http://www.github.com/SpiffyJr/SpiffyForm</a>. SpiffyForm is a touch outdated and I feel this RFC better reflects what I (and guilehermeblanco, from his message on the ML) envision forms being like. Note: even with the automated builder you could completely bypass the functionality and instantiate Zend\Form per normal. This is simply an additional option to build forms quickly and easily.</p>

  11. Feb 25, 2012

    <p>The RFC reads as though it implies hydration works only with public model properties. Is this the case?</p>

    1. Feb 27, 2012

      <p>No, I was just in a hurry. I can update it...</p>

  12. Feb 28, 2012

    <p>Any thoughts on how Zend_Dojo_Form would be impacted by these changes? I'm not sure if there's been any discussion around the Zend_Dojo component for ZF2 yet but assuming it is going to be kept then whatever the plan is for forms would need to work for the Dojo form component as well.</p>

    1. Mar 20, 2012

      <p>Shouldn't impact it that much, though getting Dojo support in place will be prioritized for <em>after</em> getting a stable form component in place. The main difference will be that decorators will not be used, and the view helpers would accept Dojo form elements and pull metadata from them to produce markup.</p>

      <p>Additionally, we need to update the Dojo component to use 1.7, which is a non-trivial task.</p>

      1. Mar 21, 2012

        <p>This may be throwing the cat amongst the pigeons but...</p>

        <p>Considering the direction ZF2 is going in and the current chatter on the ML about separating the Service components might it not be better to offer the Dojo/Jquery based stuff as a separate components/modules.</p>

        <p>That way it becomes an <em>optional</em> choice, can be updated independently of the framework, etc, etc... you get the idea.</p>

        1. Mar 21, 2012

          <p>I've actually been thinking along those lines myself. <ac:emoticon ac:name="smile" /></p>

  13. Mar 20, 2012

    <p>I have one suggestion. I don't propose solution, unfortunatelly. </p>

    <p>Validating JS generated group of elements using Zend\Form. Typical use case - product has a parameter. Parameter values are added into textboxes. There is a link "add another field", which creates new textbox. Validating such construct was always complicated in ZF, as one needed to either validate manually or save number of textboes in hidden field, recreate them and attach them to the form. </p>

    <p>Having some simplified way to do this would be great. Any ideas?</p>

    1. Mar 21, 2012

      <p>Create a "form" that only deals with the values from such input, and have those inputs come in as an array. Loop over the array, passing in the data from each to this "form", and validate each individually.</p>

  14. Mar 21, 2012

    <p>@Matthew:</p>

    <h5>Contexts</h5>

    <p>What say you to "contexts" (RE: <a class="external-link" href="http://framework.zend.com/wiki/display/ZFDEV2/RFC+-+Forms?focusedCommentId=46793017#comment-46793017)?">http://framework.zend.com/wiki/display/ZFDEV2/RFC+-+Forms?focusedCommentId=46793017#comment-46793017)?</a></p>

    <h5>Element Metadata vs. Attributes</h5>

    <p>Regarding element "metadata" (action, method, id, etc.), perhaps the term "attributes" have a bit more parity with what we are actually depicting. Thoughts?</p>

    <h5>Retrieving Values</h5>

    <p>I think a slightly modified API could be nice here:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // Grab the values. If not bound to a model, returns an array; otherwise,
    // this returns the model object
    $values = $form->getData();
    ]]></ac:plain-text-body></ac:macro>

    <p>I was thinking something more like:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // get the filtered key-value pairs
    $values = $form->values();

    // you can also pass FORM::VALUES_FILTERED to be explicit (optional)
    $values = $form->values(FORM::VALUES_FILTERED);
    ]]></ac:plain-text-body></ac:macro>
    <p>or</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    // get the raw (unfiltered) key-value pairs
    $form->values(FORM::VALUES_RAW);
    ]]></ac:plain-text-body></ac:macro>

    <h5>Validation Groups API</h5>

    <p>IMHO, this:</p>

    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $form->setValidationGroup(['username', 'password', 'password_confirmation']);
    $form->setValidationGroup([])
    ]]></ac:plain-text-body></ac:macro>

    <p>would be a nicer API to express intent than:</p>
    <ac:macro ac:name="code"><ac:plain-text-body><![CDATA[
    $form->setValidationGroup('username', 'password', 'password_confirmation');
    $form->setValidationGroup(Form::VALIDATE_ALL)
    ]]></ac:plain-text-body></ac:macro>

    1. Mar 21, 2012

      <p>Wil – unless I'm missing something big, I think the concept of validation groups largely answers the concept of "contexts" you raised - it allows specifying a selection of elements to validate and, if desired, bind to a model.</p>

      <p>Re: attributes vs. metadata – that works for me, and retains a relationship to ZF1. I'll update with that information.</p>

      <p>Re: retrieving values – I like the idea of being able to indicate raw vs. normalized. That said, i think the "getData()" method makes more sense here than "values()", as it allows dual-purpose usage to simply retrieve values or retrieve the model to which the values are bound. I think having a flag for "VALUES_AS_ARRAY" would also work nicely here, as it could force retrieving an array vs. the bound model.</p>

      <p>Re: validation groups API. We're targeting 5.3, not 5.4, so the short array notation is a no-go. <ac:emoticon ac:name="smile" /> I designed the method with the idea of using func_get_args, as this makes it possible to omit the array() syntax; having a constant value to override previous settings is considered a good practice – and will likely map to a simple scalar value as well if you want to omit the constant notation.</p>

      1. Mar 21, 2012

        <blockquote><p>the concept of validation groups largely answers the concept of "contexts"</p></blockquote>

        <p>True...I wrote the context reply, then read about "setValidationGroup" and meant to delete the note about "context", but forgot <ac:emoticon ac:name="smile" /></p>

        <p>+1 for "setValidationGroup" and given your reasoning, I am fine with the non-array API. I realize I wrote the 5.4 syntax, but with the idea that if you were using 5.4 you'd get the nice syntax, but if not, you just need to write "array()"... Neither here nor there at this point though.</p>

        <blockquote><p>Re: attributes vs. metadata – that works for me, and retains a relationship to ZF1. I'll update with that information.</p></blockquote>

        <p>Nice.</p>

        <blockquote><p>Re: retrieving values – I like the idea of being able to indicate raw vs. normalized.</p></blockquote>

        <p>Cool.</p>

        <blockquote><p>That said, i think the "getData()" method makes more sense here than "values()", as it allows dual-purpose usage to simply retrieve values or retrieve the model to which the values are bound. I think having a flag for "VALUES_AS_ARRAY" would also work nicely here, as it could force retrieving an array vs. the bound model.</p></blockquote>

        <p>I can live with that.</p>

  15. Mar 21, 2012

    <p>What about HTML5 form elements? Number, email, etc.?</p>

    1. Mar 21, 2012

      <p>We should likely add these – if nothing else, we should add view helpers for each.</p>

      1. Mar 21, 2012

        <p>It would be cool to have some default filters/validators on them - such as EmailAddress for input[type=email] element or Digits for Number element.</p>

        <p>I came accross an implementation for ZF1 <a class="external-link" href="http://www.enrise.com/2010/12/html5-zend-framework-form-elements/">http://www.enrise.com/2010/12/html5-zend-framework-form-elements/</a></p>

  16. Mar 27, 2012

    <p>Would it be possible to bind a model with a relational dataset into the form factory builder? Or am I opening a can of worms?</p>

    <p>When adding a "User" to my system they could be required to complete a "UserProfile" upon registration. I may want to drive the annotations from each of these models into ONE form.</p>

    <p>Something I could do within "My\Form\Definition"? </p>