Controller action handlers
Overview
Action handlers in HTML_QuickForm2_Controller define what should happen when request to a script containing the form is made. To make Controller execute a specific action handler after form submit you give a special name to a form's submit button:
<?php
$form->addElement('submit', $this->getButtonName('foo'),
array('value' => 'This button does foo!'));
?>
Action handlers are called via HTML_QuickForm2_Controller_Page::handle()
<?php
$page->handle('foo');
?>
which is usually done by HTML_QuickForm2_Controller::run() after finding page id and action name from
request, but can also be done manually. In fact, built-in handlers liberally call other action
handlers, the execution ending with either 'display'
,
'jump'
or 'process'
.
Action handlers can be added either to the form Page or to the Controller:
<?php
$page->addHandler('foo', new SpecificActionFoo());
$controller->addHandler('foo', new GenericActionFoo());
?>
When Page's handle() method is called it first checks for a handler added via HTML_QuickForm2_Controller_Page::addHandler() and calls its perform() method if present. If a handler is missing it calls Controller's handle() which checks for a handler added via HTML_QuickForm2_Controller::addHandler() and calls its perform() method. If a handler is missing here but its name is known and a default handler is available (see below), it is loaded and added automatically, otherwise an Exception is thrown.
Built-in action names and action handlers
Built-in name | Default handler | Description |
---|---|---|
'back' |
HTML_QuickForm2_Controller_Action_Back | This handler should be bound to the "Back" button of
(usually) a wizard-type multipage form, it redirects to the previous page whether current
page is valid or not. |
None, actual name should be equal to id of some
form page. |
HTML_QuickForm2_Controller_Action_Direct | Used to go to a specific page of the form, most useful for non-wizard forms, obviously. |
'next' |
HTML_QuickForm2_Controller_Action_Next | This handler should be bound to the "Next" button of
(usually) a wizard-type multipage form, it redirects to the next page if the current
one is valid (or the form is not wizard). On the last page of a multipage form it behaves
like 'submit' . |
'submit' |
HTML_QuickForm2_Controller_Action_Submit | This handler 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. Default handler checks whether all the pages of the form are valid, then
either calls the 'process' handler or displays the invalid
page. |
Built-in name | Default handler | Description |
---|---|---|
'display' |
HTML_QuickForm2_Controller_Action_Display | Displays the form using Default renderer. You should subclass this handler and override its renderForm() method if you want to customize the form output. |
'jump' |
HTML_QuickForm2_Controller_Action_Jump | Performs a HTTP redirect to a given page. |
'process' |
None, application-specific | 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. |
Writing a custom handler
Basically you need to create a class implementing HTML_QuickForm2_Controller_Action and add necessary logic to its perform() method.
If you intend to bind this action to some submit button via getButtonName(), you should make sure that you store the submitted values in the session container. This is most easily done via HTML_QuickForm2_Controller_Page::storeValues().
As usual, see the built-in handlers' source for the inspiration.
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 pass the action name to the controller (possibly by setting a name of some hidden element to that name).
URLs
If an instance of HTML_QuickForm2 is not provided with an explicit
'action'
attribute it tries to guess it from environment using
$_SERVER['PHP_SELF']
. This may not be a best guess when all requests are
routed through index.php
.
Controller has even more problems since its 'jump'
handler has to build an
absolute URL for a redirect (per RFC 2616). It uses various values from
$_SERVER
array for this, but your server may be configured in such a way (e.g.
if you use reverse proxy) that an URL a user sees is quite different from one a script can guess.
Thus the only bulletproof solution sometimes will be to explicitly set the
'action'
attribute of all HTML_QuickForm2 instances in
Controller:
<?php
foreach ($controller as $page) {
$page->getForm()->setAttribute('action', 'http://example.com/pretty/url/of/my/form');
}
?>
Default handler for 'jump'
will use this absolute URL for redirects.
Pretty URLs
A common question is whether it is possible to get rid of "ugly" GET
parameters that appear when redirecting from one form page to the other:
http://example.com/wizard.php?_qf_page2_display=true
The simplest solution will be to get rid of 'jump'
altogether and use
'display'
in its place. However, this will completely break navigation with
browser's "Back" and "Forward" buttons, so don't do that.
It is possible to use an URL like
http://example.com/wizard/page2
instead of the above one, but it'll be more difficult:
- Make sure that the "pretty" URL actually routes to a script containing the Controller.
- Create a custom
'jump'
handler that'll redirect to a "pretty" URL instead of an "ugly" one. - Either change HTML_QuickForm2_Controller::getActionName() to be able to get action name from a
"pretty" URL or add a key with "ugly" action name to
$_REQUEST
.