Jun 15, 2012

Resolving parameters as action-method arguments with the Yii-framework

I recently returned to PHP to work on a Yii-project, and my little 2-year detour to ASP.NET/MVC taught me some new ideas.

One of them is the idea of "model-binding", which basically means, taking GET or POST data from an HTTP request, and binding the values to model-objects in your application - prior to action-methods being invoked.

One of the advantages of this approach, is it makes action-methods (potentially) easier to test - and it also takes out some of the repetitive work of just fetching objects based on the same primary keys and, checking for permissions etc., at the beginning of every action-method.

First, let me give you an example of how to use this:

Basically, you override the CController::beforeAction() method, and call the new resolve() method for each GET argument you wish to resolve as an action-method argument.

The resolve() method takes the following arguments:

  1. $param_name name of the input $_GET[] variable to resolve - typically something that ends in "_id".
  2. $argument_name the name of the resolved action-method argument; defaults to $param_name without the "_id" suffix.
  3. $fn a Controller method-name, an anonymous function/closure, or a callback-array.

Only the first argument is required - the other two arguments can be left out, and will be established using conventions. This example shows four different ways to bind $_GET['document_id'] to the Document argument of the actionShow() method:

  • Simply pass 'document_id' as the only argument (for $param_name), and the remaining arguments will be established by convention: $argument_name defaults to e.g. 'document_id' without the '_id' suffix, as this is the most likely use-case. And $fn defaults to a camel-cased version of $param_name, prefixed with the word 'resolve'.
  • The second example is equivalent to the first, just explicitly specifying arguments equivalent to conventions.
  • The third example is also equivalent to the first and second, but demonstrates that you can also pass an array, e.g. a callback pseudo-type - enabling you to use a static method, for example.

In a nutshell, the resolve() method takes $_GET[$param_name] and passes it to your resolver-method or function, which returns the resolved value. It then makes sure that the resolved value gets passed to action-methods that take an argument with a name matching $argument_name.

Here's the source-code for a sample Controller-class integrating those changes:

Note that this feature can not really be packaged as a module or extension, because it relies on directly overriding certain functions in CController - to integrate this into your application's Controller base-class, simply copy/paste all the methods.

If you already have overrides in places for some of these methods, you may have to work a bit harder to integrate this.

Happy coding!

No comments:

Post a Comment