This document is based on questions asked on PEAR general mailing list. You are encouraged to search the list archives to find more verbose answers and examples.
The package is so complex! I don't understand anything!
Sorry, HTML_QuickForm_Controller is not intended for PHP newbies. If you don't understand what classes are, if you have no prior experience with QuickForm, if you are a fan of copy-paste programming then this package is not for you.
The package is indeed complex, but so are the problems it is trying to solve. Try to rewrite any of the enclosed multipage form examples without using such a package and you'll see what we mean.
Meta-question: actions
To avoid confusion lets use the terms "action" for the string containing the name of the action and "action handler" for the subclass of HTML_QuickForm_Action.
Default actions and default action handlers
While you can invent your own action names and create custom handlers for them, you'll most certainly have to deal with the default actions and their handlers first.
Default action names
'display'
This action has a default handler, HTML_QuickForm_Action_Display, which displays the form using the Default renderer. You should subclass this handler if you want to customize the form output. This action is called automatically when the page needs to be displayed and should not be bound to buttons via getButtonName().
'submit'
This action should be bound to a "global" submit button for a form. It can be the "submit" button of a single-page or tabbed multi-page form, "finish" button of a wizard. This action has a default handler, HTML_QuickForm_Action_Submit, which checks whether all the pages of the form are valid, then either calls the 'process' handler or displays the invalid page.
'next'
This action should be bound to the "Next" button of (usually) a modal multipage form (AKA wizard). This action has a default handler, HTML_QuickForm_Action_Next, which checks the validity of the current page and redirects to the next one if the current is valid (or if the form is not modal). On the last page of a modal multipage form this behaves like the default 'submit' handler.
'back'
This action should be bound to the "Back" button of (usually) a modal multipage form (AKA wizard). This action has a default handler, HTML_QuickForm_Action_Back, which redirects to the previous page if one exists. As of QFC 0.9.3, no form validation takes place on 'back' action.
'jump'
This action has a default handler, HTML_QuickForm_Action_Jump, which just makes HTTP redirect to the given page of the form. This action should not be bound to buttons via getButtonName().
'process'
This is the action called by default 'submit' and 'next' (on the last page of the wizard only) handlers after successful (i.e. without validation errors) form submit. This action doesn't have a default handler, you should define the custom one yourself and implement all the necessary logic to process the form's values in it.
There is also a default handler, HTML_QuickForm_Action_Direct, that does not have a default action name. It is used to go to the specific page of the form and should be explicitly added via Page::addAction() or Controller::addAction() with the name of the target page as $actionName and bound to buttons via getButtonName() using the same name.
Controller's handle() method will be called automatically if needed.
How to make a custom action?
Create a subclass of HTML_QuickForm_Action, add the necessary logic to its perform() method. Add it to the Page or to the Controller via addAction() with the appropriate name.
If you intend to bind this action to some button via getButtonName(), you should care about storing values in the container():
Build the form.
Get a reference to container.
Fill the container's ['values'][$pageName] and ['valid'][$pageName] elements.
As usual, see the default action handlers' source for the examples.
Can actions be bound to something other than submit buttons?
Controller is able to properly handle actions bound to <input type="image" /> controls, too. You don't have to do anything special, just set the control's name via getButtonName().
If you want to bind an action to something like a hyperlink, you must consider the following: the form must be submitted to be able to get its values, thus you need to write some javascript that will submit the form and somehow pass the action name to the controller.
How do I reset the form?
Controller keeps the form data and validation status in session in so-called container, accessible via
container()
method. To reset the form you need to clear this container, which is achieved by passing TRUE to
container().
How to pass additional data between pages of the form?
The answer is quite simple: you need to use the container() method of HTML_QuickForm_Controller, like this:
// Note the reference
$data =& $page->controller->container();
$data['_my_stuff'] = $stuff;
Please use some prefix for your fields or start their names with underscore to prevent possible name conflicts with the future versions of QFC.
Please note that Controller knows nothing about your additional data, so do not expect it to return it via exportValues() method or the like. You'll have to use the container() method and extract the data yourself. However, your data will be deleted when the container is reset.
How to use template-based renderers?
First of all, please understand that HTML_QuickForm_Page is a subclass of HTML_QuickForm, thus everything that applies to the parent class will apply to the child. You are encouraged to read the section about renderers in HTML_QuickForm and the docs to the renderer you are trying to use. The usage examples are available in docs/renderers/ directory of QuickForm distribution.
As was already pointed out, you should subclass the HTML_QuickForm_Action_Display class if you want to customize the form output and add an instance of this subclass as a handler for Page's 'display' action. Its _renderForm() will contain all renderer-specific code.
The only new problem Controller introduces is the buttons bound to specific actions via getButtonName(). If you are using some kind of Dynamic renderer this will not be a problem, as you need not care about the elements names, but you need these names to output the form via Static renderer.
We'll assume using ITStatic renderer but the same can be applied to other Static renderers as well. You basically have 3 options:
Manually add the buttons/placeholders with the names that should be autogenerated via getButtonName() to the form. This is not recommended, as the names should not be of interest to anyone except Controller itself and should not be used directly.
Put the auto-named buttons into group. You can name the group any way you like, just be sure to set $appendName to false. When ITStatic sees {form_element_html} placeholder, it'll assign group's HTML to it.
Call getButtonName() from within template. Add the following to the template
This code will work with HTML_Template_Sigma, HTML_Template_ITX (note the X) will require some more tweaking.
How to create a wizard with optional pages?
Thanks to Donald Lobo for this contribution.
Lets assume we have a three-page wizard:
Page 1 has a control that lets the user choose the next page.
Based on input, the user is directed to either page 2A or page 2B.
Both page 2A and 2B go to page 3 (this is commonly referred to as a
StateMachine in programming books).
You need to create several action handlers based on QFC's default HTML_QuickForm_Action_Next and HTML_QuickForm_Action_Back. You can do this either at the Page level or at the Controller level. The Page level subclassing is simple, but if you have a complex StateMachine you will end up having multiple subclasses.
For the above example, the 'next' action on page 1 sends the 'user' to either page 2 or page 3 based on input. The handler also sets the 'valid' flag of the page that will not be visited.
The 'back' action on pages 2A and 2B sends the user back to page 1, while the 'next' action sends him to page 3.
Finally, the 'back' action handler on page 3 should examine the input on page 1 and send the user to either 2A or 2B.
The working example is available in statemachine.php file.
I use client-side validation. How do I switch it off for 'Back' button?
Client-side validation is called by form's onSubmit handler, if you remove
the handler, the validation will not run. Therefore you need to remove it if user clicks on
'Back' button. This can be achieved f.e. by adding the following as
the button's onClick handler: