compared with
Current by Ryan Mauger
on Nov 17, 2009 10:51.

Key
This line was removed.
This word was removed. This word was added.
This line was added.

Changes (71)

View Page History
{toc:minLevel=2}
<ac:macro ac:name="toc"><ac:parameter ac:name="minLevel">2</ac:parameter></ac:macro>
<p>Zend_Form has been accused of violating the principal of separation of concerns, due to the fact that it has Model-related logic (filtering and validation, metadata), as well as View-releated logic (rendering, decorators).</p>

<p>While Form objects may be used to provide validation to domain models, many do not like this linking of concerns, and would prefer the ability to attach validation chains from models to the form in order to provide validation error hinting; at the same time, many others feel that forms as simply collections of elements with no ability to validate makes them basically worthless.</p>

<p>Finally, the decoration system, while powerful and flexible, is also quite difficult for many to learn. Having it coupled to Zend_Form causes some issues, as it basically inverts the decorator principal, and is somewhat misnamed as it primarily acts as a filter chain.</p>

h2. Recommendations
h3. Refactor Form view helpers to accept form/element/group objects
<h2>Recommendations</h2>
<h3>Refactor Form view helpers to accept form/element/group objects</h3>
<p>Currently, we use the ViewHelper decorator with most form elements as the first decorator. While this works well, it also complicates the issue when you want to build your markup manually; you are forced to use the ViewHelper decorator if you want to use a view helper to render the element.</p>

<p>One simple way to solve this is to modify the individual {{form*()}} <code>form*()</code> view helpers to accept elements (or other Zend_Form objects), then we can simplify things greatly. As an example:</p>

{code:php}
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$element->setOptions(array(
'size' => 25,
));
echo $view->formText($element);
{code}
]]></ac:plain-text-body></ac:macro>

h3. Use PubSub as basis for Decorator Chains
<h3>Use PubSub as basis for Decorator Chains</h3>
<p>If we use the pubsub's {{filter()}} <code>filter()</code> method, we can achieve the same effect as the current decorators. As an example:</p>
{code:php}
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
// Assuming zend\form\DecoratorChain extends pubsub\FilterChain, and that
// render() proxies to filter():
$chain->setView($view);
echo $chain->render($element);
{code}
]]></ac:plain-text-body></ac:macro>
<p>We would provide some pre-formulated chains as part of this initiative. This would then entail simply setting the view and passing in the element.</p>
{code:php}
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$chain = new zend\form\decorator\DefinitionListChain;
$chain->setView($view);
echo $chain->render($element);
{code}
]]></ac:plain-text-body></ac:macro>

<p>The above promotes re-use of decorator chains with multiple elements, and also re-use of pre-defined chains.</p>

<p>The actual act of rendering would then be removed from {{Zend_Form}} itself. <code>Zend_Form</code> itself.</p>

h3. Use PubSub as basis for Filter and Validation Chains
<h3>Use PubSub as basis for Filter and Validation Chains</h3>
<p>If we use the pubsub's {{filter()}} <code>filter()</code> method, we have all the functionality of the current {{Zend_Filter}} <code>Zend_Filter</code> and {{Zend_Validate}} <code>Zend_Validate</code> chains. This can allow us to detach chains or attach chains. As such, we would define chains and attach them to the form elements.</p>
{code:php}
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$vChain = new zend\validator\ValidatorChain();
$vChain->subscribe('Int');
$fChain->subscribe('StringTrim');
$element->setFilterChain($fChain);
{code}
]]></ac:plain-text-body></ac:macro>
<p>This allows us to continue to perform validation and filtering as before, but offloads much of the code and functionality to other, reusable components.</p>

h3. Allow <h3>Allow retrieving and setting all element validators and filter chains</h3>
<p>In order to achieve full separation, we would need the ability to retrieve these for the entire form for re-use in Models to validate data sets, as well as to pull from Models in order to attach to Forms. In essence, we're talking about an update to {{Zend_Filter_Input}}. <code>Zend_Filter_Input</code>.</p>

The basics would work as follows:
{code:php}
<p>The basics would work as follows:</p>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
// Retrieve filter and validator chains for all elements in form
$filterChain = $form->getFilterChain();
// Attach to model:
$model->setFilterChain($filterChain);
{code}
]]></ac:plain-text-body></ac:macro>

The object structure would look as follows:
{code:php}
<p>The object structure would look as follows:</p>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
zend\FilterChain::__set_state(array(
"foo" => zend\filterchain\Element::__set_state(array(
)),
))
{code}
and would be used (roughly) as follows:
{code:php}
]]></ac:plain-text-body></ac:macro>
<p>and would be used (roughly) as follows:</p>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$chain->isValid($values);
$value = $chain->get($key);
$chain->setAll($values);
$elementChain = $chain->getChain($key);
{code}
]]></ac:plain-text-body></ac:macro>

h3. Make the metadata/behavior separation exlicit
<h3>Make the metadata/behavior separation explicit</h3>
<p>The various classes in {{Zend_Form}} <code>Zend_Form</code> basically contains metadata and behavior. The metadata is typically consumed by decorators in order to determine how to render themselves; the behavior is generally related to validation and/or error message retrieval (which is a related task).</p>

<p>As such, we propose that all Form classes will define a "metadata" &quot;metadata&quot; property that will contain key/value pairs, and a set of setter/getter methods for the metadata (with specific setters for metadata that requires normalization).</p>

As an example:
{code:php}
<p>As an example:</p>
<ac:macro ac:name="code"><ac:default-parameter>php</ac:default-parameter><ac:plain-text-body><![CDATA[
$form->setMetadata(array(
'action' => $url,
));
$element->setName('foo');
{code}
]]></ac:plain-text-body></ac:macro>

h3. No IDs by default
<h3>No IDs by default</h3>
<p>Currently, the various {{Zend_Form}} <code>Zend_Form</code> elements all generate IDs. With modern JS libraries, this is typically unnecessary, and having explicit IDs makes it very difficult to have forms with variable numbers of the same element or group of elements. Removing ID generation would solve a number of UI issues. (Note: IDs could be set manually as metadata; alternately, {{setName()}} <code>setName()</code> could be overridden in order to set the ID as well.)</p>

h3. Translation
<h3>Translation</h3>
<p>Translation should be moved to the view layer. The View and Decorator chains would receive the Translator object and use it to translate appropriate labels and metadata. As such, the form, its elements, and the individual validator and validator chains would need no such knowledge of this information.</p>

h2. BC Concerns
h3. Configuration
<h2>BC Concerns</h2>
<h3>Configuration</h3>
<p>Since the chains would be separate objects, and the interaction with those chains would no longer be part of the various Form objects, creation of the chains from configuration will no longer be possible.</p>

<p>One way to mitigate this would be to have a "form builder" &quot;form builder&quot; class that can build the Form objects and all related objects from configuration. Decorators would be one of the exceptions to this, as the Decorator chains would no longer be contained within the various Form objects. As such, a "decorator chain" &quot;decorator chain&quot; builder could be useful.</p>

h3. Metadata
<h3>Metadata</h3>
<p>If metadata is moved to an explicit "metadata" &quot;metadata&quot; container, this may cause problems with existing configuration. A legacy builder class could alleviate this problem, however.</p>

h3. Removal of IDs
<h3>Removal of IDs</h3>
<p>Removing IDs may have UI implications. In particular, the Dojo integration relies heavily on the IDs in order to work. As such, this feature may need to be configurable.</p>