Adding custom action to yii\rest\ActiveController
This is just a memo for future self of what steps must be undertaken in order to:
- Add a new custom (own) action to given yii\rest\ActiveController and
- Making it accessible for selected routes / verbs / methods
If you’re familiar with the above then you won’t find anything valuable in this article, sorry.
Contents
The controller
We start with a classic and nearly empty yii\rest\ActiveController:
<?php namespace api\controllers; use yii\rest\ActiveController; class UserController extends ActiveController { public $modelClass = 'common\models\User'; }
The:
- Namespace (here:
api\controllers
) and - Used model (here:
common\models\User
)
Must of course match application layout for given scenario. Here we’re using a Yii 2 Advanced Project Template with added new (fourth) application named api for serving REST request only.
The action
Next step is to add your own custom action:
public function actionTest() { return ['test']; }
We must alter verbs()
in order to make this custom action accessible:
public function verbs() { $verbs = parent::verbs(); $verbs['test'] = ['GET', 'POST', 'DELETE']; return $verbs; }
If you want it to be accessible via GET
method only then it seems that you don’t have to specify verbs()
.
The route
Finally, we have to modify / add extraPatterns
of rules
of yii\web\UrlManager:
'extraPatterns' => [ 'GET test' => 'test', ],
The whole configuration may look similar to this:
'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'controller' => 'user', 'extraPatterns' => [ 'GET test' => 'test', ], ], ], ],
This will make REST route user/test
accessible via GET
method (like in this example).
It seems that with many controllers, custom actions and supported methods, your configuration can quickly grow really big.
Standard parameters
By “standard” I mean a classic id
. Let’s say that you want to make your custom action accepting $id
parameter:
public function actionTest($id) { return ['test' => $id]; }
We don’t have to change verbs()
in this case, but we must change extraPatterns
of rules
of yii\web\UrlManager:
'extraPatterns' => [ 'GET test/{id}' => 'test', ],
With such configuration you can send parameterized requests and get your response with parameters as well:
Non-standard token
The store gets a little bit different, if you want to use a non-standard variable for your custom action, i.e.:
public function actionTest($count = 10) { return ['count' => $count]; }
Nothing changes in the controller itself, but changes to main configuration file are a little bit bigger, since you have to define your non-standard token first:
'tokens' => [ '{id}' => '<id:\\w+>', '{count}' => '<count:\\w+>', ],
And then configure routing:
'extraPatterns' => [ 'GET test' => 'test', 'GET test/{count}' => 'test', ],
So the whole configuration would look like this:
'urlManager' => [ 'enablePrettyUrl' => true, 'enableStrictParsing' => true, 'showScriptName' => false, 'rules' => [ [ 'class' => 'yii\rest\UrlRule', 'controller' => 'user', 'tokens' => [ '{count}' => '<count:\\w+>', ], 'extraPatterns' => [ 'GET test' => 'test', 'GET test/{count}' => 'test', ], ], ], ],
With above configuration you can call your endpoint with both count
specified and set to default:
- http://localhost/yii-advanced/api/web/users/test (returns:
{count: 10}
) - http://localhost/yii-advanced/api/web/users/test/123 (returns:
{count: 123}
)
Parsing JSON input
Suppose you want your custom action to parse following structure:
{ "one": 11, "two": 2, "three": 33 }
First step is to register yii\web\JsonParser
as an application request’s parser in order to enable JSON input:
'request' => [ 'parsers' => [ 'application/json' => 'yii\web\JsonParser', ] ]
Second step is to read body parameters to get our hands on this sent JSON:
$r = Yii::$app->request; $one = $r->getBodyParam('one', 1); $two = $r->getBodyParam('two', 2); $three = $r->getBodyParam('three', 6);
You need to define Yii
class:
use Yii;
and you need to do something with the input (manipulate it or return etc.):
return [ 'params' => [ 'one' => $one, 'two' => $two, 'three' => $three, ] ];
This allows us to read the JSON input:
parse it, work on it and return it:
Otherwise you’re good and your controller’s action is now processing a custom JSON.