I thought I would post some code to see if we could move toward an Application controller to put behind the skeleton Front Controller. Like the Front Controller code, I think the Application Controller should be a bunch of little classes that work together to make the whole. I know kyberfabrikken is a fan of small classes (probably more than I am
) and I think the Front Controller shows that it is a good goal for classes.
Because an Application Controller is much more complicated than the Front Controller I think it should be done in pieces that communicate with each other with clean interfaces. This is for a couple of reasons: smaller pieces are easier to test and not everyone needs the whole Application Controller so the parts should be useful.
The code below is an example of what I will call an Input Controller. I consider something like this to be the front-end request processor for any controller., So the Input Controller might also be inherited by other special purpose controllers like a Form Controller, Pager/List Controller, Upload/Download Controller, etc.
All I want the Input Controller to do is filter and validate the Request parameters I am interested in and then let me know if there were any problems. Filtering and validating the request is usuallly at the top of lists of most important best practices for PHP programmers
I used general purpose Filter and Validator classes and then have the Input Controller manage things. Here is the code:
Filter class:
PHP Code:
class Filter {
var $chain = array();
function addFilter (&$filter) {
if (is_array($filter)) {
$this->chain = array_merge($this->chain, $filter);
} else {
$this->chain[] = $filter;
}
}
function doFilter ($value, $filter=null) {
if ($filter) {
$this->chain = $filter;
}
foreach ($this->chain as $filter) {
$value = $filter->doFilter($value);
}
return ($value);
}
} // end class Filter
Validator class:
PHP Code:
class Validator {
var $chain = array();
var $errmsg = array();
var $error = 0;
function addRule(&$rule) {
if (is_array($rule)) {
$this->chain = array_merge($this->chain, $rule);
} else {
$this->chain[] = $rule;
}
}
function validate ($value, $rule=null)
{
if ($rule) {
$this->chain = $rule;
}
$this->errmsg = array();
$this->error = 0;
foreach ($this->chain as $rule) {
if (! $rule->check($value)) {
$this->errmsg[$this->error] = $rule->getMessage();
}
++$this->error;
}
return $this->error;
}
function isError ()
{
return $this->error;
}
function getMessage ()
{
if ($this->error) {
return $this->errmsg;
}
return '';
}
} // end class Validator
Input Controller class:
PHP Code:
class InputController {
var $params = array();
var $error = false;
function InputController() {
}
function addParameter($name, &$object) {
if ($name && $object) {
$this->params[$name] =& $object;
}
}
function & getParameter($name) {
if (! isset($this->params[$name])) {
$this->addParameter($name, new InputControllerParameter($name));
}
return $this->params[$name];
}
function processRequest($request) {
$filter =& new Filter();
$validator =& new Validator();
$this->errmsg = array();
$this->error = false;
foreach ($this->params as $key => $param) {
$this->params[$key]->value = $filter->doFilter($request->get($param->name), $param->filters);
$validator->validate($this->params[$key]->value, $param->rules);
if ($validator->isError()) {
$this->params[$key]->errmsg = $validator->getMessage();
$this->error = true;
} else {
$this->params[$key]->errmsg = array();
}
}
return ! $this->error;
}
function isError() {
return $this->error;
}
function getMessage() {
$errmsg = array();
if ($this->error) {
foreach ($this->params as $key => $param) {
if ($param->errmsg) {
$errmsg[$key] = $param->errmsg;
}
}
}
return $errmsg;
}
} // end class InputController
It would be used like this:
PHP Code:
$controller =& new InputController();
$param1 =& $controller->getParameter('field1');
$param1->addFilter(new MyFilter());
$param1->addRule(new MyValidator('Error message for Field 1'));
$controller->processRequest(new Request());
if ($controller->isError()) {
$error_msg_array = $controller->getMessage();
}
This is pretty minimal code (e.g. no way to clear any of the chains, get values from the controller, etc. etc.).
A full set of code with an example, rules, filters and unit tests is here:
Input Controller example
For those who have lost track there is a CVS snapshot of the skeleton Front Controller here:
Skeleton Front Controller
Bookmarks