Mapping request parameters to action method parameters
Recently I have been looking into ASP.NET MVC implementation and I have to admit it is pretty neat. One thing I liked in particular about it is that they map request parameters (the arguments usually passed in the URL) directly to the method parameters. In Zend Framework it is required to use the getParam method on the request object
$this->_request->getParam('name', 'default');
Being a developer I decided to add same feature to Zend Framework as well. Basically my solution uses reflection API to discover parameters, type hints and optional default values.
Here is a simple example of this in action
/**
* Use default value:
* URL: index/view
* OUPUT: int(1)
* Pass page value (Rewrite)
* URL: index/view/page/2
* OUTPUT: int(2)
* Classic method (GET)
* URL: index/view/?page=3
* OUTPUT: int(3)
* Pass invalid (string) value
* URL: index/list/page/invalid
* OUTPUT: int(0)
*
* @param int
*/
public function indexAction($page = 1)
{
Zend_Debug::dump($page);
die;
}
This highlights couple of aspects. First you can provide type hinting in phpdoc comment and it will automatically cast argument to the given type. This may seem as a tedious extra writing, but it does encourage to use method documentation. However due to way Zend Framework reflection API works the @param in the comments must be in exactly same order as parameters in the method. Names are not matched!
Some would argue that this syntactic sugar decreases performance due to extra work involved and Reflection uses. I agree with this –it does incur some overhead. However considering that Zend Framework requires a pretty complicated boilerplate code to actually reach your action this here is miniscule by comparison. Besides cashing can be used to speed things up.
Here is another more advanced example
/**
* index/info/
* index/info/tags/one/tags/two
* index/info/msg/msg-only
* index/info/date/2009-10-03
* index/info/msg/RandomOrder/date/2009-10-03
*
* @param array $tags
* @param Zend_Date $date
* @param string $msg
*/
public function infoAction(array $tags = null, Zend_Date $date = null, $msg = 'hello')
{
Zend_Debug::dump($tags);
if (!is_null($date)) echo $date->get(Zend_Date::DATETIME_FULL);
else echo "no date passed\n";
Zend_Debug::dump($msg);
die;
}
You can easily pass arrays and objects and mapper will automatically do appropriate casting and initialization. The order in which arguments appear in the URL is not important and any of them can be missing in which case default value will be used. When using class or array type hint then phpdoc comments are not really necessary however due to reflection API limitation parameters must appear in the same order. Since msg is last and we want it to be a string we need to add $tags and $date beforehand.
However be vary when instantiating classes directly in this manner. Since the input is not validated you can easily get hard to track errors or exceptions.
By default missing parameters (even if method prototype doesn’t have a default value) will be silently ignored and a default value assigned (empty array, null or an object instance). However you can optionally specify a required variable and have the mapper throw an error if parameter doesn’t exist.
/**
* @require_page Exception
* @param int
*/
public function testAction($page)
{
Zend_Debug::dump($page);
die;
}
Simply add @require_<param here name> [and optionally exception class name to throw] into the phpdoc. With this in place if $page is not passed mapper will throw an exception.
To use this download the package below and simply extend your class from ZendX_Controller_Action
Download it from here: Action.zip