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
switch ($_REQUEST['action']) {
'foo':
doFoo();
break;
'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
To ease understanding of this package's features, lets take an example form from
HTML_QuickForm tutorial and
redo it using HTML_QuickForm_Controller:
Example 41-1. Basic Controller usage // 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:
$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
$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
$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
$page =& new FirstPage('firstForm'); |
and add our custom action handler to it
$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
$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
$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
which will take care of finding the name of the current action and calling the necessary handler. That's all.