Class to handle form input

Are there any approaches out there to take Form input and put it into a Class/Object so that it can then be worked with at the Object level?

A few weeks ago I seem to have written a Class to put Form inputs into an array in the Object but I forget where that is.

Your suggestions and ideas are welcome.

TomTees

I take a similar approach to oddz. Markup forms in XML. It promotes better separation of concerns than having lots of PHP code doing that generation.

You can then easily re-initialise the form on subsequent requests populate the form from $_POST/$_GET where required.

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.

I just wanted to quote this because I agree. Trying to automate processing just isn’t scalable as there are too many possible variables. Display can often be automated but you also need ways to override it completely. (E.g. sometimes you’ll want columns of checkboxes or radio buttons which are displayed horizontally rather than vertically, etc)

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.

Well you could do an input library and a form validation library.

The form validation library would use a custom config array for each row and define each field you want to exist, what type of data type it should be, what callbacks you want to enact on it, and if it is required or not. Codeigniter uses this type of form validation.

Then you could have an input library that strips the $_POST super and it returns the value if present.


if($this->form_validation->run() == false) {
$data['first_name'] = set_value('first_name');
$data['last_name'] = set_value('lastname');
}
else {
toModel['firstname'] = $this->input->post('first_name');
toModel['lastname'] = $this->input->post('last_name');
}

set_value would return the original form input that you want sent back to be displayed in the form. If you didn’t want anything for a specific field say a password and password conf box you’d just set that to an empty string after the other set_value’s you did want sent back to the user form.

I would suggest looking at the Forms component of the Symfony2 framework.

http://docs.symfony-reloaded.org/guides/forms.html
http://www.slideshare.net/bschussek/the-new-form-framework

Thanks for the suggestions, but all of that is way over my head at this point… :frowning:

TomTees