[[toc]] + Horde_Form rewrite/Horde_Field:: Proposal+ Horde_Form rewrite/Horde_Field:: Proposal ThisThis page is a proposal for the implementation of new package to replace Horde_Form_Type:: and Horde_Form_Variable::.I'm refactoring this page into topics. I had a major wiki breakthrough when doing this for work (as I receive e-mail, enter meeting notes, etc., I now factor into topics which could be different pages or different headings on a page). A really good overview emerges fairly quickly and it works extremely well with something like a project's needs analysis. So let's call it a pattern. -- JasonFelice++ Proposed Paths/Requirements+++ AutogenerationExtendExtend Rdo (http://cvs.horde.org/framework/Rdo) and Horde_Form to autogenerate basic CRUD forms (create, read, update, delete) based on the metadata already available in Rdo.+++ Ajax FunctionalityFollow up on the Summer of Code Horde_Form project (currently available in the incubator - http://cvs.horde.org/incubator/Horde_Form/). Update the code to use Prototype andScriptaculous for the scripting basis, sync with the current Horde_Form code, and go from there. The ability to have any form switch between active and inactive rendered mode - optionally on a field-by-field basis - using in-place-edit functionality would be very slick.++++++ Useable Without Horde_Form::ThereThere are currently still some interdependencies with Horde_UI_VarRenderer:: and Horde_Form:: which really suck. When working on Horde_UI_Table::, I need to render fields in display mode and I have no form. -- JasonFelice+++ No Display AssumptionsI'm going to take the opportunity of a Text_Wiki design discussion on the PEAR lists to propose we reorganize our whole concept of varrenderers and form types, etc.First of all, we should clean up the form types so that they are solely conceptual - no display assumptions/logic/differences in them. No difference between radio and select lists, for example.Then we have the field renderers/etc., one renderer per type of field (where radio and select lists *are* different) per rendering format supported (XHTML, PDF, etc).It'dIt'd be a lot of files, which I've tried to avoid in the past, but it seems the only way to really make this flexible, and to avoid having to parse *too* much code just to get a select widget, for example. This should be useable with things like Horde_UI_Table, the Prefs system, the Blocks configuration, etc., too, without intrisically requiring Horde_Form.I'mI'm not sure where the renderers should live in the class tree.The Text/Wiki class tree idea that got me thinking about this again looks likeso:<code>Text/Wiki/Parse.phpText/Wiki/Parse/Bold.phpText/Wiki/Parse/Code.php Text/Wiki/Parse/Wikilink.phpText/Wiki/Parse/Wikilink.php Text/Wiki/Render.phpText/Wiki/Render.php Text/Wiki/Render/DocBook.phpText/Wiki/Render/DocBook/Bold.phpText/Wiki/Render/DocBook/Code.phpText/Wiki/Render/DocBook/Wikilink.php</code>-- ChuckHagenbuch (from e-mail on May 26, 2004)+++ Multiple Output FormatsIn our old framework, we can render a field to plain text, HTML, or !TeX (since that is how we produce PDFs). Here's one question, how do we handle edit formats? We could theoretically have a PDF edit format or a Flash edit format, or more likely some sort of embedded device markup. If we have an output matrix of ( medium * type ), we could be talking about a lot of classes. I propose that we just have one class per output medium, and display or edit is determined by which method is called. -- JasonFeliceHere's one question, how do we handle edit formats? We could theoretically have a PDF edit format or a Flash edit format, or more likely some sort of embedded device markup. If we have an output matrix of ( medium * type ), we could be talking about a lot of classes. I propose that we just have one class per output medium, and display or edit is determined by which method is called. -- JasonFelice+++ "Filter" Fields I don't know how to handle this just yet, but in our old framework, we have a Get_Filter_HTML() method. For a date, it returns HTML with a start date field and an end date field (it is a possibly open-ended date range, in other words). The field also has a Get_Filter_SQL() which takes a field name and returns a fragment of an SQL WHERE clause to "filter" by that expression. In Horde, we can't assume the back end is SQL. In the old framework, a lot of the Get_Filter_HTML() methods have been factored into separate field types (like a date range field, in this case), so perhaps just a method to retrieve an instance of the filter field type for this field type. The driver model makes it difficult to automagically turn this into a query limiter, though, so it might just not be feasable for Horde.+++ "Filter" Fields I don't know how to handle this just yet, but in our old framework, we have a Get_Filter_HTML() method. For a date, it returns HTML with a start date field and an end date field (it is a possibly open-ended date range, in other words). The field also has a Get_Filter_SQL() which takes a field name and returns a fragment of an SQL WHERE clause to "filter" by that expression. In Horde, we can't assume the back end is SQL. In the old framework, a lot of the Get_Filter_HTML() methods have been factored into separate field types (like a date range field, in this case), so perhaps just a method to retrieve an instance of the filter field type for this field type. The driver model makes it difficult to automagically turn this into a query limiter, though, so it might just not be feasable for Horde. ---- JasonFelice+++ Field Representation Is Configurable In order to take into account different sorts of representations for the same data type, the fields can accept field-dependant config parameters (the typical $params key/value array). The field can use these to determine things like:+++ Field Representation Is Configurable In order to take into account different sorts of representations for the same data type, the fields can accept field-dependant config parameters (the typical $params key/value array). The field can use these to determine things like: * How wide a text field appears.* Whether to use radio buttons or a drop-down for an enum.* etc.-- JasonFelice+++ Scatter/GatherI think the field class that returns the HTML edit representation needs to have a method for "gathering" the form fields back into the internal representation. An example could be Horde's monthdayyear field, which has three form fields for representing a date value. The gather() method would take this data and return the internal value. -- JasonFelice+++ Careful Attention to Internal Value RepresentationsWe'veWe've had this problem with our old framework, and I see it to some degree in Horde. What does a date look like? If we could pick one form, we constrain the number of interfaces we deal with and therefore the complexity. In our old framework, we have internal (PHP) values, database (!PostgreSQL textual representation) values and HTML form values. We assumed when first designing when we were basically just using text inputs that all of these were the same, but that got us into big touble.In Horde, we can make sure the storage driver returns the internal form, so we seal off the database representation into the database-specific driver, which is really nice. We currently do have a problem with the other types of values, though. We should probably always use PEAR Date:: representations because we certainly don't want different fields for UNIX timestamp and more useful date formats.-- JasonFelice++ Unsorted Noteshttp://uipatternfactory.com/ http://www.lukew.com/resources/articles/PSactions.asphttp://jeffhowden.com/code/css/forms/http://www.frevvo.com/frevvo/web/homehttp://www.smashingmagazine.com/2006/11/11/css-based-forms-modern-solutions/http://www.formassembly.com/form-garden.phphttp://www.jamiehoover.com/?channel=journal&item=11http://code.google.com/p/repetitionmodel/http://www.nyphp.org/phundamentals/spoofed_submission.phphttp://svn.rubyonrails.org/rails/trunk/actionpack/lib/action_view/helpers/form_helper.rbhttp://ajaxian.com/archives/ajform-and-rejax-reloadedHorde_Form - change tabs and tab javascript to use HTML structure from YUI:http://developer.yahoo.com/yui/tabview/Also use this for Prefs?Also consider Ext tabs:http://www.yui-ext.com/deploy/ext-1.0-alpha3/docs/Additional tabs notes:http://developer.yahoo.com/ypatterns/pattern.php?pattern=moduletabsavoidavoid serialized form data, send a checksum for it (checksum in session of course)Horde_ViewHorde_View and Horde_Form and sub-viewsdon't need to create new view objects!just call $this->render() with the sub page name!view variables will be maintainedI just discovered a hole in a white list validation technique I borrowed from a PHP security book ‹ no, not Chris¹ book.PHP security book ‹ no, not Chris¹ book. BewareBeware in_array($_POST/GET[Œinput¹], $whitelist)Type matters. All input is string type and PHP will try to force typematching.So the input string Œsecurityhole¹ will match the int number 0.horde_formhorde_form overloading:$form->Enum($name,$form->Enum($name, $desc, $note, ... (any other params are type config options - use func_get_args/func_num_args if needed - not needed, have __call $args$form->optionalEnum - non required$form->requiredEnum - requiredwhich is default?expected variables - track in the form, not the Variables objectneed a simplified Variables object for incubator Horde_Form- remove foo[bar] syntax in favor of $vars->foo->bar ?- remove expected variables, getExists, etc.- could just be an ArrayObject?Look at what should be in Type and what in Variable simplify validate() methods (rename from isValid?)simplify validate() methods (rename from isValid?) http://blog.astrumfutura.com/archives/281-Complex-Web-Pages-with-the-Zend-Framework.html Horde_Form rewrite notes:http://blog.astrumfutura.com/archives/281-Complex-Web-Pages-with-the-Zend-Framework.html Horde_Form rewrite notes: multienum (and checkboxes if any?) get consolidated to Setenum, radio buttons -> enumHorde_Form1 class/file for autoload__autoload file (all types?)Horde_Form_Variable_View ?rename Variable to Field?keep type separate but make basic api as clean and small as possibleHorde_Form_Type -> standard constructor for all types instead of factorynono singletons!horde_form_type_* -> replace about() methods with doc introspectionhttp://us2.php.net/reflection form descriptions - xml, db, php (the list of addVariable() calls)form descriptions - xml, db, php (the list of addVariable() calls) TODOTODO Actions for buttons (not currently possible)I know this sounds really dumb but I cant seem to find a solution forit. How do I attach an action (Horde_Form_Action) to a submit buttonin a form that is generated via the Horde_Form class? My objective isto call a custom javascript function when the user clicks the submitbutton on the form.i am currently in the need of a form-library to process form-data without the ever goinghassle of boring input sanitization and validation. i read thru the Zend-Form proposaland it seems that its going to be a *big* form-creation quick-form pendant.i coded a component to do filtering and validation only and transporting values anderrors over session-namespaces to redirect between processing<->view.the usage is like this:<code type="php">// FORM-PROCESSING$F = new Form('edit');// common form-data$F->register('type')->isTableKey('mediatype', 'mediatype_id');$F->register('collection')->isBool();$F->register('parts')->isOptional()->isArray()->isTableKey('media', 'media_id');$F->register('lang')->isTableKey('locale', 'locale_id');$F->register('country')->isTableKey('locale', 'locale_id');$F->register('title')->isString();$F->register('origtitle')->isString();$F->register('comment')->isString();$F->register('rating')->isInt(0, 5);// optional checklists (kind of <select multiple>)$F->register('genres')->isOptional()->isArray()->isTableKey('genre', 'genre_id');$F->register('genres_new')->isOptional()->isArray()->isString();$F->register('persons')->isOptional()->isArray()->isTableKey('person', 'person_id')->requires('person_roles');$F->register('person_roles')->isOptional()->isArray() ->isString();->isArray() ->isString(); $F->register('persons_new')->isOptional()->isArray()->isString()->requires('person_roles_new');$F->register('person_roles_new')->isOptional()->isArray()->isString(); [...][...] //// working with Zend-Validators, -Filters and Function-Delegatesfunction fooDelegate($elementName, $elementValue, $formData) {return true;}$F->register('foo')->validate(new Zend_Validate_xxx())->filter(new Zend_Filter_yyy())->delegate('fooDelegate');try {$this->db->beginTransaction();$F->validate($_POST);$title = $F->value('title');...if (($persons= $F->value('persons')) {foreach ($persons as $personID) {...}}//or$persons = $F->value('persons', array());foreach ($persons as $personID) {...}} catch (FormException $e) {$this->db->rollBack();print_r($F->getErrors());$this->redirect('back to edit-form-view');}// VIEW$F = new Form('edit');if ($F->hasErrors())print_r($F->getErrors());if (!($title = $F->value('title')))$title = '<populate from db>';<input type=text value=$title /></code>APIAPI discussion:We are in the middle of a discussion Alexey and I about QuickForm2 API for elementscreation and I would like your opinion as well.At this point, nothing is immutable since we aren't even talking about alpha stage, soyour preferences as users and developers is interesting.To summarize, Alexey would be in favor of this:$form->addElement('button', 'aButton', 'Click me please');$form->addElement('select', 'aSelect', array('1' => 'option 1', '2' => 'option 2'));While I would be in favor of this:$form->addElement('button', 'aButton')->setContent('Click me please');$form->addElement('select', 'aSelect')->setOptions(array('1' => 'option 1', '2' =>'option 2'));This is an example for adding a button to a form. The API can get more complex for Dateelements and other javascript aided elements.My opinion is that we shouldn't mix configuration parameters for elements with other kindof data they might need.The other point we are discussing is about the extra parameter in element creation. Isuggest we always use an array, even when there is only one extra parameter. Alexeysuggests that we use a scalar if there is only one extra parameter. For example, for agiven "Year" element which would only accept one configuration parameter 'startYear',Alexey would use:$form->addElement('year', 'aYear', '2007');While I would use: $form->addElement('year', 'aYear', array('startYear' => '2007'));$form->addElement('year', 'aYear', array('startYear' => '2007')); SoSo my way is more verbose and less writable, but is also more readable and extensible.Given these examples, are there any opinions or preferences in favor of one or the otherproposed API ?Thanks in advance,We are in the middle of a discussion Alexey and I about QuickForm2API for elements creation and I would like your opinion as well.At this point, nothing is immutable since we aren't even talkingabout alpha stage, so your preferences as users and developers isinteresting.To summarize, Alexey would be in favor of this:$form->addElement('button', 'aButton', 'Click me please');$form->addElement('select', 'aSelect', array('1' => 'option 1', '2'=> 'option 2'));This is the old (and IMO sometimes confusing) style which is (at leastin the current QF) not consistent through the various elements.While I would be in favor of this:$form->addElement('button', 'aButton')->setContent('Click me please');$form->addElement('select', 'aSelect')->setOptions(array('1' =>'option 1', '2' => 'option 2'));This style is more "OO-ish" (*g*) and should also be more consistent inusage.The other point we are discussing is about the extra parameter inelement creation. I suggest we always use an array, even when thereis only one extra parameter. Alexey suggests that we use a scalar ifthere is only one extra parameter. For example, for a given "Year"element which would only accept one configuration parameter'startYear', Alexey would use:$form->addElement('year', 'aYear', '2007');WhileWhile I would use:$form->addElement('year',$form->addElement('year', 'aYear', array('startYear' => '2007'));+1 for this last style because it avoids confusion, too, and especiallyalso because sometimes later such elements might get a second, third,... option.What's really being discussed is the following, will we make the "additional data"parameter that has different semantics from element to element always an array, so thefirst pair of calls above turns into$form->addElement('button', 'aButton', array('content' => 'Click me please'));$form->addElement('select', 'aSelect', array('options' => array('1' => 'option 1', '2' =>'option 2')));even though the elements in question will never need any additional data other than'content' and 'options'.My vote would be for always an array --- even in cases where anelement will only need one piece of scalar data.As many have already pointed out, changing from scalars to array basedon the individual element leads to confusion about which elementsrequire which parameter format --- I would even be against theflexibility of allowing both ways because I think it leads to morecomplicated documentation and more room for coding errors.I've honestly found the addElement() API to be difficult to work with inthethe past, so simplifying it would be a welcome change.However, I don't think "addElement" needs to exist in Quickform2. Haveyou considered an API similar to the following?<?php$form = new HTML_Quickform2;$form['action'] = '/path/to/action.php';$form['method'] = 'post';// or$form['attrs'] = array('action' => '/path/to/action.php', 'method' =>'post');$g = $form->group['mygroup'];$g->text = array('name' => 'aText', 'size' => 50, 'default' => 'whatever');$g->select = array('name' => 'aSelect', 'options' => array(...));// or$g->text(array('name' => 'aText', 'size' => 50))->validate['regex'] = '/\w+/';$g->select(array('name' => 'aSelect', 'options' => array())$select = $g->select['aSelect']; // or $form->select['aSelect'];$select->options['another'] = 1;$select->validate['custom'] = 'is_numeric';?>Compare this to:<?php$form = new HTML_Quickform2('/path/to/action.php', 'method' => 'post');$form->addGroup('mygroup');$form->addElement('text', 'aText', array('size' => 50, 'default' =>'whatever', 'group' => 'mygroup')->validate('regex', '/\w+/');$sel = $form->addElement('select', 'aSelect', array(...));$sel->addOption('another', 1);$sel->validate('custom', 'is_numeric');?>In the second example, my eyes are drawn to a bunch of "add*" and itappears that the important information ('text', 'select') is secondary.It takes me a while to figure out what is actually being done. Thefirstfirst example has no superfluous information, but simply sets up theformform in a straightforward, no-nonsense manner.In other words, I've always dreamed of having a simplexml-like API foraccessing/creating forms. I hate translating what I want to do intomethod APIs. Think of this as bringing the REST model to formscreation, whereas everything has been XML-RPC based up to this point :).Another feature request I've had that may be possible is to integratesome kind of forms creation cache, so that the form need only be createdprogrammatically once, and then it can on subsequent requests be quicklyrecreated from a disk or database cache, skipping all the expensivemethod calls, but this is a separate question than the one you asked,let me know if you'd like a feature request opened on this.If you do decide to go with the other API format, I am in favor of moreexplicit options (associative array/fluent). I hate relying on Zend IDEto do auto-completion, it makes debugging far more difficult to do withjust a simple text editor and eyeballs.As an addendum to my last message, I just thought of an even clearer interface:interface: <?php $form<?php $form = new HTML_Quickform2;$g = $form->group['mygroup'];$g = $form->group['mygroup']; $g->text['aText'] = array('size' => 50);$g->select['aSelect'] = array('size' => 5', 'options' => array(...));?>By using ArrayAccess offsetSet() you can put the element name inbrackets. This way, the same procedure (array access) is used to bothset and to retrieve elements. This could also be used to modify valueslater if necessary, for the same element, something that I've found exceedingly difficult to do in the current QF. Consider the following: <?phpexceedingly difficult to do in the current QF. Consider the following: <?php$form = new HTML_Quickform2; $form->text['mytext'] = array('default' => 'whatever'); unset($form->text['mytext']); ?> Simple and easy.$form->text['mytext'] = array('default' => 'whatever'); unset($form->text['mytext']); ?> Simple and easy. LeLe 16 avr. 07 ? 18:31, Gregory Beaver a Ècrit :> $g->text(array('name' => 'aText', 'size' => 50))> ->validate['regex'] = '/\w+/';> $g->select(array('name' => 'aSelect', 'options' => array())> $select = $g->select['aSelect']; // or $form->select['aSelect'];> $select->options['another'] = 1;> $select->validate['custom'] = 'is_numeric';I like this as well. We have designed what I would call a low-levelAPI for QuickForm. Built on top of that, it will be easier to addsyntactic sugar like this at a later time.It's a bit like what jQuery and Prototype did to javascript DOM. PHP5allows this, we should indeed use it to our advantage IMO (I need toconvince Alexey though :) ).> Another feature request I've had that may be possible is to integrate> some kind of forms creation cache, so that the form need only be> created> programmatically once, and then it can on subsequent requests be> quickly> recreated from a disk or database cache, skipping all the expensive> method calls, but this is a separate question than the one you asked,> let me know if you'd like a feature request opened on this.At the moment, I have no idea how to do that, any ideas would bewelcome.The good news is that QuickForm2 should be faster than currentQuickForm :)Another feature request I've had that may be possible is to integratesome kind of forms creation cache, so that the form need only be createdprogrammatically once, and then it can on subsequent requests be quicklyrecreated from a disk or database cache, skipping all the expensivemethod calls, but this is a separate question than the one you asked,let me know if you'd like a feature request opened on this.At the moment, I have no idea how to do that, any ideas would be welcome.The good news is that QuickForm2 should be faster than current QuickForm :)Hi Bertrand,I would store element validation information in a simple array, and thenuse the renderer to render a PHP-based template (a la Savant) that willallow setting default values, so that the display process is basically:<?php$form->prepareCache();include $form->getCacheFile();?>Validation would work as it currently does. You'd end up with somethinglike: <?phplike: <?php $form = new HTML_Quickform2;$form['name'] = 'whatever';if ($form->submitted && $form->valid) {}} if ($form->cached) {$form->displayCache();} else {// set up the form}?>Things like validation errors can be handled with placeholders as well (an if() in the template that skips the error HTML).(an if() in the template that skips the error HTML). To summarize, Alexey would be in favor of this:To summarize, Alexey would be in favor of this: $form->addElement('button', 'aButton', 'Click me please');$form->addElement('select', 'aSelect', array('1' => 'option 1', '2'=> 'option 2'));Well, this has worked in the past and isn't too hard to grasp if thedocumentation makes it clear how this works (the current docs are abit confusing in this regard). Having the additional parameters be theconstructor args for the element is a fine option. However, it'salways confusing to have lots of parameters to a method and I know Inever remember which is which. $form->addElement('year', 'aYear', '2007');$form->addElement('year', 'aYear', '2007'); WhileWhile I would use:$form->addElement('year', 'aYear', array('startYear' => '2007'));$form->addElement('year', 'aYear', array('startYear' => '2007')); So my way is more verbose and less writable, but is also morereadable and extensible.Extensability is a good thing. Readability is a good thing. I'd votefor an assoc array over variable numbers of arguments.Well, people aren't yet familiar with the new QF2 API and Bertrand didn't bother to givethe full picture, so here goes:We no longer have variable number of parameters to elements' constructors, since neitherBertrand nor myself were able to remember all the parameter lists, and we actually*wrote* the abomination. Every constructor is now defined as:public function __construct($name, $data, $label, $attributes) { ... }Now, this what this $data thing means changes from element to element because theelements have different needs. What's *really* being discussed here is the fact thatBertrand wants to make $data *always* an associative array, thus$form->addElement('button', 'aButton', array('content' => 'Click me please'));$form->addElement('select', 'aSelect', array('options' => array('1' => 'option 1', '2' =>'option 2')));while I suggest deciding on it on a per-element basis, thus$form->addElement('button', 'aButton', 'Click me please');$form->addElement('select', 'aSelect', array('1' => 'option 1', '2' => 'option 2'));but most certainly$form->addElement('year', 'aYear', array('startYear' => '2007'));since that hypothetical 'year' element *may* earn additional parameters in the futurewhich is highly unlikely in case of Buttons and Selects.I've honestly found the addElement() API to be difficult to work with inthe past, so simplifying it would be a welcome change.However, I don't think "addElement" needs to exist in Quickform2. Have you considered an API similar to the following?you considered an API similar to the following? <?php<?php $form = new HTML_Quickform2;$form['action'] = '/path/to/action.php';$form['method'] = 'post';//// or$form['attrs'] = array('action' => '/path/to/action.php', 'method' =>'post');$g = $form->group['mygroup'];$g->text = array('name' => 'aText', 'size' => 50, 'default' => 'whatever');$g->select = array('name' => 'aSelect', 'options' => array(...));// or$g->text(array('name' => 'aText', 'size' => 50))->validate['regex'] = '/\w+/';$g->select(array('name' => 'aSelect', 'options' => array())$select = $g->select['aSelect']; // or $form->select['aSelect'];$select->options['another'] = 1;$select->validate['custom'] = 'is_numeric';?>With all due respect, SimpleXML API which you seem to use as inspiration here usesaddAttribute() and addChild() methods for adding attributes and child nodes, it doesn'twork as shown above. ;) We considered something like that, but decided to go with DOM-like API for now.We considered something like that, but decided to go with DOM-like API for now.I assume it is more consistent to always use an array and provide additional setters forall possible keys, so every element can be added in both ways:$form->addElement('button', 'aButton', array('content' => 'Click me', 'attributes' =>array('onClick' => 'alert("Clicked!")')));or:$clickbutton = $form->addElement('button', 'aButton');$clickbutton->setContent('Click me');$clickbutton->setAttributes('onClick' => 'alert("Clicked!")');Like this, every public property can actually be set on element construction.Anyway I see an other point I want to strongly advise to rethink: It looks like the QF2API will still be quite near to the HTML syntax of every single element, which isactually not consistent across the elements and their natures. I gather this assumptionfrom the "options" property of the select element (besides: setOptions() also confuses asthis method has a totally different meaning in many other PEAR classes).If you put your focus at interchangability of elements, you find 2 different kinds oflists, which can both have 2 different forms:1. List with only one element selectable:- Select element with size=1 (or no size attribute)- Radiobutton group2. List with multiple selectable elements:- Select element with size > 1- Checkbox groupIMOIMO those should be treated identically, for example:$ctry = $form->addElement('select', 'country');$ctry->setList(array('CH' => 'Switzerland', 'FR' => 'France'));$ctry->setContent('CH');If you later decide to change it to a radiogroup, you just have to change the line:$ctry = $form->addElement('radiogroup', 'country');Also note that I suggest to use setContent() to define the preselected option rather thansomething like $option->setSelected() (which could be left as an alternative). It looksconsistent to me to set the preselected value with the same method for all kinds ofelements/groups, regardless of the way it looks in the generated HTML. So setContent() ina select box will result in setting the selected attribute, while it will set a valueattribute if applied on a text field.II would even make it:$clickbutton =$form->addElement('button', 'aButton')->setContent('Clickme')->setAttributes('onClick' => 'alert("Clicked!")');On 4/17/07, Christoph Schiessl <c.schiessl@gmx.net> wrote:[Show Quoted Text - 58 lines]On Apr 17, 2007, at 9:58 AM, Markus Ernst wrote:Alexey Borzov schrieb:[...]What's really being discussed is the following, will we make the"additional data" parameter that has different semantics fromelement to element always an array, so the first pair of callsabove turns into$form->addElement('button', 'aButton', array('content' => 'Clickme please'));$form->addElement('select', 'aSelect', array('options' => array('1' => 'option 1', '2' => 'option 2')));even though the elements in question will never need anyadditional data other than 'content' and 'options'.I assume it is more consistent to always use an array and provideadditional setters for all possible keys, so every element can beadded in both ways:$form->addElement('button', 'aButton', array('content' => 'Clickme', 'attributes' => array('onClick' => 'alert("Clicked!")')));I don't like the idea of using arrays for the additional parameters.Element creation has always been a problem for me in QF1, becausethere were to many possible combinations of parameters to rememberall of them.or:$clickbutton = $form->addElement('button', 'aButton');$clickbutton->setContent('Click me');$clickbutton->setAttributes('onClick' => 'alert("Clicked!")');Like this, every public property can actually be set on elementconstruction.Looks nice. I think that would be easier to use (and to remember).Also note that I suggest to use setContent() to define thepreselected option rather than something like $option->setSelected() (which could be left as an alternative). It looks consistent tome to set the preselected value with the same method for all kindsof elements/groups, regardless of the way it looks in the generatedHTML. So setContent() in a select box will result in setting theselected attribute, while it will set a value attribute if appliedon a text field.Fully agree on that one.--MarkusI cannot say I support setting variables and properties with arrays,it also have been the biggest thing that kept me away from QF for along time.Bertrand Mansion wrote:Hi all,We are in the middle of a discussion Alexey and I about QuickForm2 APIfor elements creation and I would like your opinion as well.At this point, nothing is immutable since we aren't even talking aboutalpha stage, so your preferences as users and developers is interesting.ToTo summarize, Alexey would be in favor of this:$form->addElement('button', 'aButton', 'Click me please');$form->addElement('select', 'aSelect', array('1' => 'option 1', '2' =>'option 2'));While I would be in favor of this:$form->addElement('button', 'aButton')->setContent('Click me please');$form->addElement('select', 'aSelect')->setOptions(array('1' => 'option1',1', '2' => 'option 2'));This is an example for adding a button to a form. The API can get morecomplex for Date elements and other javascript aided elements.My opinion is that we shouldn't mix configuration parameters forelements with other kind of data they might need.The other point we are discussing is about the extra parameter inelement creation. I suggest we always use an array, even when there isonly one extra parameter. Alexey suggests that we use a scalar if thereis only one extra parameter. For example, for a given "Year" elementwhich would only accept one configuration parameter 'startYear', Alexeywould use:$form->addElement('year', 'aYear', '2007');WhileWhile I would use:$form->addElement('year',$form->addElement('year', 'aYear', array('startYear' => '2007'));So my way is more verbose and less writable, but is also more readableand extensible.Given these examples, are there any opinions or preferences in favor ofone or the other proposed API ?Hi Bertrand,I've honestly found the addElement() API to be difficult to work with inthethe past, so simplifying it would be a welcome change.However, I don't think "addElement" needs to exist in Quickform2. Haveyou considered an API similar to the following?<?php$form = new HTML_Quickform2;$form['action'] = '/path/to/action.php';$form['method'] = 'post';// or$form['attrs'] = array('action' => '/path/to/action.php', 'method' =>'post');$g = $form->group['mygroup'];$g->text = array('name' => 'aText', 'size' => 50, 'default' => 'whatever');$g->select = array('name' => 'aSelect', 'options' => array(...));// or$g->text(array('name' => 'aText', 'size' => 50))->validate['regex'] = '/\w+/';$g->select(array('name' => 'aSelect', 'options' => array())$select = $g->select['aSelect']; // or $form->select['aSelect'];$select->options['another'] = 1;$select->validate['custom'] = 'is_numeric';?>Compare this to:<?php$form = new HTML_Quickform2('/path/to/action.php', 'method' => 'post');$form->addGroup('mygroup');$form->addElement('text', 'aText', array('size' => 50, 'default' =>'whatever', 'group' => 'mygroup')->validate('regex', '/\w+/');$sel = $form->addElement('select', 'aSelect', array(...));$sel->addOption('another', 1);$sel->validate('custom', 'is_numeric');?>In the second example, my eyes are drawn to a bunch of "add*" and itappears that the important information ('text', 'select') is secondary.It takes me a while to figure out what is actually being done. Thefirst example has no superfluous information, but simply sets up theform in a straightforward, no-nonsense manner.In other words, I've always dreamed of having a simplexml-like API foraccessing/creating forms. I hate translating what I want to do intomethod APIs. Think of this as bringing the REST model to formscreation, whereas everything has been XML-RPC based up to this point :).Using AAs for creating forms is absolutely the way to go. A huge +1for Greg's suggestions. It is exactly because of the reasons Gregmentions. The form creation and the actual display of the form shouldbe separated completely.Another feature request I've had that may be possible is to integratesome kind of forms creation cache, so that the form need only be createdprogrammatically once, and then it can on subsequent requests be quicklyrecreated from a disk or database cache, skipping all the expensivemethod calls, but this is a separate question than the one you asked,let me know if you'd like a feature request opened on this.This is *exactly* what Solar[1] does. You can load[2] forms (orrather, form "hints", which are just AAs) from SQL table objects andfrom XML files and hand them to your "view". This is *made* for MVC.This complicated API that you suggest seems a really bad idea to me.Anyways, just my $0.02[1][1] http://solarphp.com[2][2] http://solarphp.com/class/Solar_Form/load()I don't find any current implementations of forms to have a true cache.The problem here is that a form is always changing. It must respond touser input, display errors, and so on. However, the logic of how theform is constructed is completely static. Since the only thing wereally need to be able to do is proper validation of form fields, andthen to fill in a few placeholders (default value, error messages ifany), this data can be cached as html, saving the need to construct ahuge tree of objects.For high-volume sites, this could provide a large gain inrequests/second, whereas building the form from an array is no differentfrom building it with method calls - except you have the added step ofparsing the array.Separating logic from view is irrelevant to my suggestion. Quickform 1.x used renderers to do this, I suppose Quickform2 will have some facility as well.