Comment 1 for bug 527946

Revision history for this message
Sander van Schouwenburg (Sandworm) (sander-sinaasappel) wrote :

My solution is to modify AnewtForm so that it has a 'prefix' element, which is actually an array (as to include nested subforms). If you have a form with the prefix array('foo', 'bar'), and a control named 'foobar', then during the rendering it will have the rendered name 'foo[bar][foobar]' and the id 'foo-bar-foobar'.

So controls have a 'name', which is just the name as normal which is unique in the form. They also have a 'rendered-name', which is the name prefixed by the form prefix as described above. All controls had to be modified to use this rendered-name.

A new control AnewtFormControlSubform is created, which takes an AnewtForm in its constructor. It also needs a renderer some time before rendering (optional constructor argument). It will set the prefix of the subform by its own prefix plus its own name. But because its own prefix is only known when the enclosing form has received its prefix, this is done as late as possible (during rendering, see open issues below).

The AnewtFormRenderer was modified so that hidden controls can be rendered separately. AnewtFormRendererDefault was modified so that all controls can be rendered without an enclosing <form> node. During rendering, the AnewtFormControlSubform will set the prefixes and render the hidden controls and the rest of the controls.

During form submission AnewtForm::autofill() will strip any custom prefixes from the $_GET or $_POST values and fill the form with the rest. AnewtFormControlSubform::set_value($value) will pass the values along to its subform and AnewtFormControlSubform::get_value() will return the form values of the subform. Normal fill handling will take care of the rest.

Validation (is_valid()) will also chain as expected to the subforms.

Processing (handle_(in)valid()) of forms is *not* chained automatically. AnewtFormControlSubform has a method 'process()' which automatically calls 'process' in the subform. This has to be called manually from for example the handle_valid() from the enclosing form (note that process() in the subform will automatically call handle_valid() or handle_invalid() depending on its validity).

Open issues:

Complex controls with multiple input tags are not yet supported in subforms. The only complex control in anewt itself is the AnewtFormControlFileUpload. The main problem here is that it creates extra controls postfixed with an attribute (like "-remove"), but that will create a name like "foo[bar][foobar]-remove", which php does not process. The simple solution (which also avoids name conflicts) will be to change them so that postfixed like "[remove]", but then the original input element must also be postfixed by something.

The prefix for the AnewtForm is set with AnewtFormControlSubform->set_prefix(), which is called during AnewtFormControlSubForm->build_widget(). So before rendering, the prefixes will not be correct unless you manually call set_prefix().

Just realized but not tested: A subform containing only controls which may not be submitted (like checkboxes and multi-select controls) will probably fail on filling when nothing is submitted (i.e.: all the checkboxes are off). Simple workaround for now is to ignore the value of $form->autofill() in that case.