As of recent I’ve come to enjoy generating my forms from configuration arrays or XML file(s). Seems like that gives me the most flexibility and reduces repetitive, boring coding procedures. Though, I tend not to automate the processing, just the display since most times there are specific implementation details of processing that can’t be made generic without resulting in a overly complex interface. For my own work all my forms are XML files. I than pass the array to a view helper that builds out the form display from the configuration. Though, the entire helper layer can be avoided, if special display based logic is necessary.
Quick example of what I mean:
<modules>
<mod pkg="PlatForm.ProjectPad.Module.Register">
<frm mixin="Component.User.Module.Registration.Form">
<first_name>
<label>First Name</label>
<required>Y</required>
<min>1</min>
<max>96</max>
<type>custom_first_name</type>
</first_name>
<last_name>
<label>Last Name</label>
<required>Y</required>
<min>1</min>
<max>96</max>
<type>user_last_name</type>
</last_name>
<states_id>
<label>State</label>
<required>N</required>
<dao>
<pkg>Component.Util.DAO.DAOUtil</pkg>
<method>listAllStates</method>
<args>
<arg>s.human_name label,s.terms_id value</arg>
<arg></arg>
<arg>s.human_name ASC</arg>
</args>
</dao>
</states_id>
</frm>
</mod>
</modules>
One thing I take particular pride in is being able to bind a data source callback to the value of a input, which turns it into a select menu. Also, I’m able to mixin other forms. For example, the form above extends the below form, without having to duplicate any code.
<modules>
<mod pkg="Component.User.Module.Registration.Form">
<frm>
<username>
<label>Username</label>
<required>Y</required>
<min>1</min>
<max>24</max>
<type>custom_username</type>
</username>
<email_address>
<label>Email</label>
<required>Y</required>
<min>1</min>
<max>128</max>
<type>email</type>
</email_address>
<pwd>
<label>Password</label>
<required>Y</required>
<min>7</min>
<max>20</max>
<type>password</type>
</pwd>
</frm>
</mod>
</modules>
The mixin system also allows me to to pretty much extend forms overriding individual elements; such as label, adding new elements or removing elements. I can also add a special property called static to any element, so that it is not controllable to the user. This comes in handy when extending the generic content form I have, for contextual based input, like products.
<modules>
<mod pkg="PlatForm.ProjectPad.Module.Project.Form">
<frm mixin="Component.Node.Module.Form.Entry">
<categories_id>
<label>Category</label>
<required>Y</required>
<after>nodes_title</after>
<dao>
<pkg>PlatForm.ProjectPad.DAO.DAOProjectPadCategory</pkg>
<method>fetchCategoryOptions</method>
<args>
<arg>SITES_ID</arg>
</args>
</dao>
</categories_id>
<ideal_price>
<label>Ideal Price</label>
<required>N</required>
<type>numeric</type>
</ideal_price>
<node_content>
<label>Project</label>
</node_content>
<node_types_id static="Y">
<default>project::PlatForm.ProjectPad</default>
</node_types_id>
<node_subtitle static="Y">
<default></default>
</node_subtitle>
<content_type static="Y">
<default>text</default>
</content_type>
<intro_type static="Y">
<default>text</default>
</intro_type>
<intro_content static="Y">
<default></default>
</intro_content>
<node_published static="Y">
<default>1</default>
</node_published>
</frm>
</mod>
</modules>
That is an example of a form to enter projects as nodes, that extends the node form. In doing so it sets static values for anything declared as static that it inherits from its mixin. Also, the default label for the Node form is Content, so that is easily changed to Project overriding the label form element.
Example of Resolved Project Form configuration:
Array
(
[node_types_id] => Array
(
[label] => Content Type
[required] => Y
[values] => Array
(
[0] => Array
(
[value] => project::PlatForm.ProjectPad
[label] => PlatForm.ProjectPad::project
)
[1] => Array
(
[value] => portfolio
[label] => portfolio
)
)
[static] => Y
[default] => project::PlatForm.ProjectPad
)
[node_title] => Array
(
[label] => Title
[required] => Y
[max] => 128
)
[node_subtitle] => Array
(
[label] => Subtitle
[required] => N
[max] => 128
[static] => Y
[default] =>
)
[node_content] => Array
(
[label] => Project
[required] => Y
[textarea] => 1
)
[intro_content] => Array
(
[label] => Teaser
[required] => N
[textarea] => 1
[static] => Y
[default] =>
)
[content_type] => Array
(
[label] => Body Type
[required] => Y
[default] => text
[values] => Array
(
[0] => Array
(
[value] => html
[label] => html
)
[1] => Array
(
[value] => php
[label] => php
)
[2] => Array
(
[value] => text
[label] => text
)
)
[static] => Y
)
[intro_type] => Array
(
[label] => Teaser Type
[required] => Y
[default] => text
[values] => Array
(
[0] => Array
(
[value] => html
[label] => html
)
[1] => Array
(
[value] => php
[label] => php
)
[2] => Array
(
[value] => text
[label] => text
)
)
[static] => Y
)
[node_published] => Array
(
[label] => Published
[required] => Y
[default] => 1
[values] => Array
(
[0] => Array
(
[label] => Published
[value] => 1
)
[1] => Array
(
[label] => Unpublished
[value] => 0
)
)
[static] => Y
)
[categories_id] => Array
(
[label] => Category
[required] => Y
[after] => nodes_title
[values] => Array
(
[0] => Array
(
[terms_id] => 293
[value] => 293
[label] => MySQL
[values] => Array
(
)
)
[1] => Array
(
[terms_id] => 294
[value] => 294
[label] => Programming
[values] => Array
(
[0] => Array
(
[terms_id] => 295
[value] => 295
[label] => MySQL
[values] => Array
(
)
)
[1] => Array
(
[terms_id] => 296
[value] => 296
[label] => PHP
[values] => Array
(
[0] => Array
(
[terms_id] => 297
[value] => 297
[label] => Drupal
[values] => Array
(
)
)
)
)
)
)
[2] => Array
(
[terms_id] => 292
[value] => 292
[label] => Web Design
[values] => Array
(
[0] => Array
(
[terms_id] => 298
[value] => 298
[label] => Drupal
[values] => Array
(
)
)
)
)
)
)
[ideal_price] => Array
(
[label] => Ideal Price
[required] => N
[type] => numeric
)
)
The whole idea here is being able to pushing the concept of inheritance to the form, configuration level to reduce replication and aid in consistency. While also allowing as much flexibility as possible. Considering, there are no hard and fast rules, everything all tools are merely there if they are needed.
This form framework I have no place allows me to very quickly create the display of new forms. While I handle the processing manually, specific to each different circumstance.
I’ve always hated declaring forms as objects on a case by case basis. using an XML files to store form configuration and fill in processing details in the controller, pushing the end result to the model is much more my taste.