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.

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.

Leave a Reply