2010-11-21

JadedPHP controllers

In my last post about JadedPHP, I talked about the model layer. Today, it's all about the controller layer.

Jaded controllers are built around the Intercepting Filter Pattern. In brief, when the Dispatcher sends a request to a controller for processing, the request is actually dispatched to a chain of filters which can modify it or check it for necessary attributes (has it been authenticated, etc.) before passing it on to the actual request handler.

A controller class extends the Jaded_Controller class, and needs to define a process() method.
class MyController extends Jaded_Controller
{
    protected function process(Jaded_Request $oRequest, Jaded_Response $oResponse)
    {
        $oResponse->assign('myVar', 'this is the assigned value');
        $oResponse->assign('requestVar', $oRequest->getParam('reqParam'));
    }
}
Here is a simple controller that assigns some values to a response variable. One of the assignments, myVar, is given by the controller. The other is taken from the passed in request object. If the name parameter was never set in the request, the getParam() call will return null.

$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

// The way we get to the process() method is through the dispatch() method
$oController->dispatch($oRequest, $oResponse);

print_r($oResponse->getAssigns());
/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 
)

*/

The requestVar is null because we never populated it. Here is a controller filter that will populate it for us:
class RequestFillerFilter extends Jaded_Controller_Filter_PreProcessor
{
    protected function preProcess(Jaded_Request $oRequest, Jaded_Response $oResponse)
    {
        $oRequest->setParam('requestVar', 123);
    }
}

//Now we modify the dispatching code from above
$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

// Create the filter, wrapping the controller that will process the request
$oFilter = new RequestFillerFilter($oController);

// Filters are actually controllers themselves, and are dispatched the same way
$oFilter->dispatch($oRequest, $oResponse);

print_r($oResponse->getAssigns());
/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 123
)

*/
requestVar was set in the request object before it was passed to the controller, so it was there for the controller to access.

Here's an example of a post-processing filter, that will handle our output for us:
class Print_RFilter extends Jaded_Controller_Filter_PostProcessor
{
    protected function postProcess(Jaded_Request $oRequest, Jaded_Response $oResponse)
    {
        print_r($oResponse->getAssigns());
    }
}

//And here is the usage
$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

// Filters expect a controller as a construction param,
// but since filters are controllers, we can chain them like this:
$oFilter = new Print_RFilter( new RequestFillerFilter( $oController));
$oFilter->dispatch($oRequest, $oResponse);

/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 123
)

*/
Notice that the dispatcher no longer needs to call print_r() itself, because the post filter will do it instead.

It gets unwieldy to have to keep wrapping controllers in filters, especially if a set of controllers always gets wrapped in the same filters. Jaded provides a Chain filter that let's you string together commonly used filters to simplify wrapping controllers in filters.
class CommonFilterChain extends Jaded_Controller_Filter_Chain
{
    protected $aFilters = array(
        'Print_RFilter',
        'RequestFillerFilter',
    );
}

$oController = new MyController();
$oRequest = new Jaded_Request();
$oResponse = new Jaded_Response();

$oFilter = new CommonFilterChain($oController);
$oFilter->dispatch($oRequest, $oResponse);

/*
Prints:
Array
(
    [myVar] => 'this is the assigned value'
    [requestVar] => 123
)

*/
It doesn't matter in this example, but filters listed in a chain are wrapped with the first listed filter as the outermost, and the last filter as the innermost. This is relevant when using a Jaded_Controller_Filter which defines both preProcess() and postProcess() methods.

The other interesting thing to note about chains is that they are filters themselves, so it is possible to have a chain filter listed inside another chain filter.

My goal in JadedPHP is to provide filters that perform tasks common to a web application and wrap them in easy to use chains. This way, the dispatcher can detect that a request is an HTTP request, and automatically wrap it in filters that will initialize the session, put the request method and parameters into a request object, set up an HTML or AJAX renderer, check for authentication and other necessary tasks.

The example source code is available here.

No comments:

Post a Comment