Model binders is a wonderful concept I discovered recently while working with ASP.NET MVC 2.
If you’re not familiar with the concept, you can read about it here.
I wrote a PHP example, just to demonstrate that this technique is now possible with PHP5…
<?php
/**
* A simple model object
*/
class User
{
public $id;
public $username;
public $password;
}
/**
* A simple controller
*/
class TestController
{
public function actionTest($id, User $user=null)
{
$user->id = $id;
var_dump($id, $user);
}
}
/**
* A very simple value provider
*/
class ValueProvider
{
public function provide(&$value, $name)
{
if (!isset($_POST[$name]))
return false;
$value = $_POST[$name];
return true;
}
}
/**
* A very basic object provider
*/
class ObjectProvider
{
public function provide(&$object, $name, $class)
{
if (!isset($_POST[$name]))
return false;
$object = new $class;
foreach ($_POST[$name] as $key=>$value)
$object->$key = $value;
return true;
}
}
/**
* A sampler dispatcher, integrating providers
*/
function dispatch($controllerId, $actionId)
{
$vp = new ValueProvider;
$op = new ObjectProvider;
$controllerName = ucfirst($controllerId).'Controller';
$methodName = 'action'.ucfirst($actionId);
$params = array();
$method = new ReflectionMethod($controllerName,$methodName);
foreach ($method->getParameters() as $p)
{
$paramName = $p->getName();
if ($pc = $p->getClass())
$className = $pc->getName();
else
$className = null;
if ($optional = $p->isOptional())
$default = $p->getDefaultValue();
else
$default = null;
if ($className)
$op->provide($params[$paramName], $paramName, $className);
else
$vp->provide($params[$paramName], $paramName);
}
$controller = new $controllerName;
call_user_func_array(array($controller,$methodName), $params);
}
// mock POST data:
$_POST['id'] = 123;
$_POST['user'] = array(
'username' => 'admin',
'password' => 'super_secret',
);
// dispatch the controller/action:
header('Content-type: text/plain');
dispatch('test','test');
Just a flat PHP script with no dependency on Yii or otherwise.
Note the simplicity of TestController::actionTest() - in this case, the $id and $user parameters have been hydrated by the sample dispatcher, using two example providers, one for values and one for objects.
This is just a proof of concept, but should be sufficient to demonstrate the elegance of this technique - even for complex objects, you would write your recipe for transforming posted form data into an object, once, in the form of a provider, and then never need to deal with that in your action methods again.
No more if (isset($_POST[‘user’])) or $_GET[‘id’] in your action methods - much cleaner.
And also makes it possible to write clean unit tests for your controllers…