Forms
From KnowledgeTree Document Management Made Simple
Displaying a form, processing its output, validating that output and handling any errors which occur along the way - these are the steps involved in the vast majority of KT UI code. To make this process less error-prone, more efficient and a whole lot less tedious, KT includes a fairly solid widget & validation layer.
Key to using that layer is the KTForm class (/lib/widgets/forms.inc.php). Using this class will allow you to use and re-use the broad range of items available in the system.
There are 4 phases to the lifecycle of any form:
- Create the form.
- Display the form
- Validate the form
- Act on that validation
Step 4 may send you back to Step 2 with errors, or proceed to a different action. Doing all this in a generic, easy to use way requires a lot of effort for each form if done manually - doing it using the Forms code requires virtually none.
Contents |
Setting up the basic form
All of the form, widget and validator functionality uses configuration arrays rather than lots of method calls or long method signatures to do its work. This makes it more flexible, and easier to read code that uses it. The basic sequence of calls is:
$oForm = new KTForm; $oForm->setOptions($aConfiguration); $oForm->setWidgets($aWidgets); $oForm->setValidators($aWidgets);
Using the form comes down to 3 basic methods. To display the form, one uses one of
$oForm->render() $oForm->renderPage($sTitle, $sDescription)
The latter is useful for all those "display just this form" situations. Validating the form (after its been submitted) uses both of the following:
$res = $oForm->validate() return $oForm->handleError($sCustomMessage, $aMoreErrors)
validate() returns an array containing two subarrays: errors and results. See "Validating the form" below for more information.
The best way to do all this is to use a separate method on the dispatcher so that you only ever define the form in one place. Typically, we call these methods "form_<actionname>" to make it clearer what the form does.
Basic Configuration
Configuring the actual form is relatively straightforward - it uses a fairly straightforward configuration array.
| item | required | type | description ! |
|---|---|---|---|
| identifier | yes | string | Its very important that each different form has a "namespaced" name. This allows the system to work out whether old data (e.g. from a previous request) is still valid |
| context | no | dispatcher | If you're creating the form from a dispatcher, you will usually need to pass around a variety of extra information (e.g. what folder / document you're on if you're an action, etc). Passing in your dispatcher as "context" will allow all that to occur automatically. You want to do this in the vast majority of situations. |
| extra_args | no | array | If for some very good reason you don't want to use the persistence built into dispatchers, you can pass in an array of key => value pairs to pass on to the form recipient. |
| label | yes | string | The form should have a clear label, describing the data being collected. See UI Guidelines for more information on how to name forms, actions, etc. |
| description | no | string | When possible, one should provide a simple introductory description explaining what the form will do. Naturally, this is not always appropriate. |
| submit_label | yes | string | What label should the "submit" action have. |
| action | yes | string | The name of the action to which this form should send its data. |
| fail_action | yes | string | When there's a validation problem, where should the form be submitted? |
| cancel_action | no | string | If the form can be cancelled, where does the cancellation send the user (action) |
| cancel_url | no | string | An alternative to the "cancel_action", sometimes you need to send the user to a completely different part of the application. This will let you do that. (e.g. KTBrowseUtil::getUrlForDocument(...)) |
Adding Widgets
Adding widgets is very straightforward, and can be done in one of 2 ways. The annoying, painful way is actually instantiate a subclass of KTWidget, configure it, and add that to the form using addWidget. The "easy" way is to simply tell the form what widgets to instantiate, and how to configure them. Since the former isn't recommended, I'll only explain how to use the "normal" mechanism.
To create widgets using the form's widget-factory, you use a set of arrays like this:
$oForm->setWidgets(array(
array('ktcore.widgets.string', array(
'label' => _kt('My Label'),
...
)),
));
Essentially, its a list of arrays of the form:
array($sWidgetNamespace, $aWidgetConfiguration)
For more information on how to configure each widget, see Widgets.
The key methods are
setWidgets($aWidgets) addWidgets($aWidgets) addWidget($mWidgetOrConfiguration)
Adding Validators
Validators are added in exactly the same way as widgets, except that you validators instead of widgets. Similarly to Widgets, the key methods are
setValidators($aValidators) addValidators($aValidators) addValidators($mValidatorOrConfiguration)
Validating the form
Validation typically has two parts
- extracting the data from widget-specific formats into something generally more useful.
- identifying an errors in the user's input
Within the form machinery, this is all done by the same set of validators, depending on how they work. Some validation is automatically handled by the widgets (e.g. a field marked as "required" will check that something was selected or input). Some needs to be done by you. For more info on what validators are available, see Validators. The most important things to remember
- extracting the data into the "results" array is done by validators with an output parameter to their configuration.
- the widgets will have massaged their data into some useful format. Check the widget's documentation for details of what that format is.
Very often the validation you need to perform doesn't fit neatly into the pre-defined validators and writing a once-off validator doesn't make much sense. In that case you can do the validation manually and pass any errors out using handleError()'s "$aExtraErrors" parameter. To do this you create an array of "widget_name" => $mMixedErrors, where the value is either a string or a list of strings to be shown as the error on a given field. "widget_name" is the "basename" of one of your widgets (e.g. the name of the value that the widget puts out).
Handling problems
If and when there's a problem, its important to do two things:
- provide the user with the best idea of both what went wrong, and how to fix it
- not lose the information the user input.
The forms machinery handles both of these for you through the use of
$sOutput = handleError($sMessage, $aExtraErrors)
Usually this is used very simply as follows
$oForm = $this->form_addmyobject()
$res = $oForm->validate()
if (!empty($res['errors'])) { return $oForm->handleError(); }
$data = $res['results'];
If you don't pass a message into "handleError" it will automatically use "Please correct the errors indicated below" as the message, and $aExtraErrors is described in the section called "Validating the form".
If the form had a "context" parameter, that will be used to error-out of the current method using errorRedirectTo()".
del.icio.us
reddit

