Controller action handlers

Controller action handlers – Using available handlers and writing custom ones

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

Handlers that can be bound to submit buttons
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.
Other handlers, only called explicitly
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:

  1. Make sure that the "pretty" URL actually routes to a script containing the Controller.
  2. Create a custom 'jump' handler that'll redirect to a "pretty" URL instead of an "ugly" one.
  3. 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.