Introduction
Why "Controller"?
This package implements a PageController design pattern, which essentially means that there is a single page processing requests and actions this page performs depend on parameters passed in GET or POST data. The pattern is described in more detail on Martin Fowler's website and WACT project website.
What does this mean in application to QuickForm: we have a single script which shows and validates different forms depending on data in request. This allows to fairly easy build very complex forms consisting of several pages (think wizards and such).
The most basic implementation of the PageController pattern would look like
<?php
switch ($_REQUEST['action']) {
case 'foo':
doFoo();
break;
case 'bar':
doBar();
break;
default:
echo 'Hello, world!';
}
?>
HTML_QuickForm_Controller is a bit more complex. There are three base classes:
- HTML_QuickForm_Controller: this class extracts the action name from request and calls the appropriate handler. It includes several Pages.
- HTML_QuickForm_Page: this class (extending HTML_QuickForm) represents a single page of a form.
- HTML_QuickForm_Action: this class implements the Command design pattern, i.e. is essentially an OO callback.
The action sent in request consists of a page name and an action name, it defines which Page will process the request (e.g. display or validate the form) and what exactly this Page should do.
Basic usage example
Session initializationThis simple example does not use sessions since there is no need to pass data between pages. You'll need to use sessions when dealing with a real multipage form, though. HTML_QuickForm_Controller does not start a session automatically, you should explicitly call session_start() before instantiating the controller class.
To ease understanding of this package's features, lets take an example form from HTML_QuickForm tutorial and redo it using HTML_QuickForm_Controller:
Basic Controller usage
<?php
// Load the controller
require_once 'HTML/QuickForm/Controller.php';
// Load the base Action class (we will subclass it later)
require_once 'HTML/QuickForm/Action.php';
// Class representing a form
class FirstPage extends HTML_QuickForm_Page
{
function buildForm()
{
$this->_formBuilt = true;
// Add some elements to the form
$this->addElement('header', null, 'QuickForm tutorial example');
$this->addElement('text', 'name', 'Enter your name:', array('size' => 50, 'maxlength' => 255));
// Note how we set the name of the submit button
$this->addElement('submit', $this->getButtonName('submit'), 'Send');
// Define filters and validation rules
$this->applyFilter('name', 'trim');
$this->addRule('name', 'Please enter your name', 'required', null, 'client');
$this->setDefaultAction('submit');
}
}
// Action to process the form
class ActionProcess extends HTML_QuickForm_Action
{
function perform(&$page, $actionName)
{
echo '<h1>Hello, ' . htmlspecialchars($page->exportValue('name')) . '!</h1>';
}
}
$page =& new FirstPage('firstForm');
// We only add the 'process' handler, Controller will care for default ones
$page->addAction('process', new ActionProcess());
// Instantiate the Controller
$controller =& new HTML_QuickForm_Controller('tutorial');
// Set defaults for the form elements
$controller->setDefaults(array(
'name' => 'Joe User'
));
// Add the page to Controller
$controller->addPage($page);
// Process the request
$controller->run();
?>
You may note that the code is more verbose than the original. That is true, but to make a three page
wizard-type form you'll only need to create three subclasses of
HTML_QuickForm_Page
and 'process'
event handler based on HTML_QuickForm_Action
and add them to Controller, while without the Controller infrastructure it will require a non-trivial amount
of programming.
Creating custom pages
You need to subclass HTML_QuickForm_Page and override its buildForm() method. Its contents are pretty self-explanatory (if you are familiar with QuickForm), except for a few things:
<?php
$this->_formBuilt = true;
?>
Form building is a "heavy" operation, thus we call buildForm() only when
necessary. To prevent calling it twice (and getting two sets of elements instead of one) we set $_formBuilt
property.
The second notable line is
<?php
$this->addElement('submit', $this->getButtonName('submit'), 'Send');
?>
We use getButtonName()
method to set the submit button's name and thus to trigger a 'submit'
action when the button
is clicked.
The third thing is
<?php
$this->setDefaultAction('submit');
?>
The user can submit the form by pressing Enter button, not by clicking on any of the form buttons. Most browsers will not consider any submit button as clicked in this case and will not send its name. setDefaultAction() sets the action (by adding a special hidden element to the form) that will be called in this case.
Creating custom actions
You'll usually need to create handlers for two actions: 'process'
and 'display'
.
While it is difficult to say anything about the former, as only you know how to process your form, for the
latter you'll need to subclass
HTML_QuickForm_Action_Display
and override its _renderForm() method to call the appropriate
Renderer and do form output customization.
Tying this all together
Next we instantiate the page class defined above
<?php
$page =& new FirstPage('firstForm');
?>
and add our custom action handler to it
<?php
$page->addAction('process', new ActionProcess());
?>
the 'process'
action will be called by the default 'submit'
action
handler if the form is valid.
Then we instantiate the controller
<?php
$controller =& new HTML_QuickForm_Controller('tutorial');
?>
Note that the name is a required parameter, and if you will have several Controllers in your application they should have different names, as the names are used to store values in sessions.
Then we set the defaults for the form and add the page to it
<?php
$controller->setDefaults(array(
'name' => 'Joe User'
));
$controller->addPage($page);
?>
It is perfectly legal to do call Page's setDefaults() from within buildForm(), but the former approach allows to set the defaults for the form as a whole, while the latter only for the page in question.
Finally we call the Controller's run() method
<?php
$controller->run();
?>
which will take care of finding the name of the current action and calling the necessary handler. That's all.
Advanced usage examples
...are available in the package archive. Along with the example similar to the provided above, there are two multipage forms:
-
Wizard: form pages contain
'Next'
and'Back'
buttons and you can't go to the next page unless the current page is valid. -
Tabbed form: form has several pages and buttons allow to go directly to the corresponding page.
Form is validated only when the global
'Submit'
button is pressed.