没有不值得去解决的问题,也没有不值得去学习的技术!

Create a GitHub Repository for yiisoft/yii2-app-advanced and Build a RESTful API with Internationalization Support

In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, with the message in Simplified Chinese

1. It is recommended to implement RESTful Web services based on a standalone API application. In this case, it is implemented based on the api application.

2. Create a new directory: \api\rests. This directory will serve as the directory for the action method classes of the RESTful Web service.

3. Create a new controller class \api\controllers\UserController.php. The controller class extends [[yii\rest\ActiveController]]. The implementation of data serialization includes pagination information in the response body to simplify client development, as shown in Figure 1.

Create a new controller class \api\controllers\UserController.php. The controller class extends [[yii\rest\ActiveController]]. The implementation of data serialization includes pagination information in the response body to simplify client development
Figure 1
PHP
<?php
/**
 * Created by PhpStorm.
 * User: WangQiang
 * Date: 2018/04/04
 * Time: 15:35
 */
 
namespace api\controllers;
 
use yii\rest\ActiveController;
 
class UserController extends ActiveController
{
    public $serializer = [
        'class' => 'api\rests\user\Serializer',
        'collectionEnvelope' => 'items',
    ];
 
    /**
     * @inheritdoc
     */
    public function actions()
    {
        return [
            'index' => [
                'class' => 'api\rests\user\IndexAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
            'view' => [
                'class' => 'api\rests\user\ViewAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
            'create' => [
                'class' => 'api\rests\user\CreateAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
                'scenario' => $this->createScenario,
            ],
            'update' => [
                'class' => 'api\rests\user\UpdateAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
                'scenario' => $this->updateScenario,
            ],
            'delete' => [
                'class' => 'api\rests\user\DeleteAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
            'options' => [
                'class' => 'api\rests\user\OptionsAction',
                'modelClass' => $this->modelClass,
                'checkAccess' => [$this, 'checkAccess'],
            ],
        ];
    }
}
 
 

Note: If only a few actions need to be supported, you can choose the following solution, for example:

PHP
    public function actions()
    {
        $actions = parent::actions();
        
        $actions['view']['class'] = 'api\rests\user\ViewAction';

        return $actions;
    }

4. When the configuration content is very complex, the common practice is to store it in one or more PHP files. These files are called configuration files. A configuration file returns a PHP array. To implement versioning, configure the URL rules by modifying the configuration of the urlManager component in the application configuration to support the v1 module and all actions. Create: \api\config\urlManager.php, as shown in Figure 2.

When the configuration content is very complex, the common practice is to store it in one or more PHP files. These files are called configuration files. A configuration file returns a PHP array. To implement versioning, configure the URL rules by modifying the configuration of the urlManager component in the application configuration to support the v1 module and all actions. Create: \api\config\urlManager.php
Figure 2
PHP
<?php
return [
    'class' => yii\web\UrlManager::class,
    'enablePrettyUrl' => true,
    'enableStrictParsing' => true,
    'showScriptName' => false,
    'rules' => [
        [
            'class' => 'yii\rest\UrlRule',
            'controller' => ['v1/user'],
        ],
    ],
];

5. Include \api\config\urlManager.php in \api\config\main.php. Edit \api\config\main.php.

PHP
        'urlManager' => require __DIR__ . '/urlManager.php',

6. Implement each major version of the API in a separate module ID with the major version number. Generate the v1 module using Gii by opening the URL: http://www.github-shuijingwan-yii2-app-advanced.localhost/gii/module. Delete the directory: \api\modules\v1\views, as shown in Figure 3.

Implement each major version of the API in a separate module ID with the major version number. Generate the v1 module using Gii by opening the URL: http://www.github-shuijingwan-yii2-app-advanced.localhost/gii/module. Delete the directory: \api\modules\v1\views
Figure 3

7. Create \api\modules\v1\models\User.php, which inherits from \api\models\User.php, as shown in Figure 4.
Note: \api\modules\v1\models\User (only for the v1 module) > \api\models\User (only for the api application) > \common\logics\User.php (can be used by multiple applications such as api, frontend, etc.) > \common\models\User.php (limited to Gii generation) > \yii\db\ActiveRecord

Create \api\modules\v1\models\User.php, which inherits from \api\models\User.php
Figure 4
PHP
<?php
/**
 * Created by PhpStorm.
 * User: WangQiang
 * Date: 2018/04/04
 * Time: 16:04
 */
 
namespace api\modules\v1\models;
 
 
class User extends \api\models\User
{
 
}
 

8. Rename \api\modules\v1\controllers\DefaultController.php to \api\modules\v1\controllers\UserController.php. By specifying [[yii\rest\ActiveController::modelClass|modelClass]] as api\modules\v1\models\User, the controller knows which model to use for fetching and processing data. Edit the code, as shown in Figure 5.
Note: \api\modules\v1\controllers\UserController.php (only for the v1 module) > \api\controllers\UserController.php (only for the api application) > \yii\rest\ActiveController

Rename \api\modules\v1\controllers\DefaultController.php to \api\modules\v1\controllers\UserController.php. By specifying [[yii\rest\ActiveController::modelClass|modelClass]] as api\modules\v1\models\User, the controller knows which model to use for fetching and processing data
Figure 5
PHP
<?php
 
namespace api\modules\v1\controllers;
 
/**
 * User controller for the `v1` module
 */
class UserController extends \api\controllers\UserController
{
    public $modelClass = 'api\modules\v1\models\User';
}
 

9. To use a module in an application, simply add the module to the list of the [[yii\base\Application::modules|modules]] property in the application configuration. The application configuration in the following code uses the v1 module. Edit \api\config\main.php, as shown in Figure 6.

To use a module in an application, simply add the module to the list of the [[yii\base\Application::modules|modules]] property in the application configuration. The application configuration in the following code uses the v1 module. Edit \api\config\main.php
Figure 6
PHP
    'modules' => [
        'v1' => [
            'class' => api\modules\v1\Module::class,
        ],
    ],

10. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 returns a 404 response in HTML format, as shown in Figure 7.

HTML
<!DOCTYPE html>
<html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <meta name="csrf-param" content="_csrf-api">
        <meta name="csrf-token" content="00ssponpmJwoWBZLFTExhai8kSgeTCkNzKenlyRbznyiAHzyz6Pc9XICYxpmWV7KzePdQnYZG2umkezQXg38Tw==">
        <title>Not Found (#404)</title>
        <link href="/assets/73866dfd/css/bootstrap.css" rel="stylesheet">
        <link href="/css/site.css" rel="stylesheet">
    </head>
    <body>
    </body>
</html>

11. To resolve the 404 response being in HTML format, edit \api\config\main.php and set the default response format to JSON.

PHP
        'response' => [
            'format' => yii\web\Response::FORMAT_JSON,
        ],

12. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 returns a 404 response in JSON format, as shown in Figure 8.

In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1 returns a 404 response in JSON format
Figure 8
JSON
{
    "name": "Not Found",
    "message": "用户未找到。",
    "code": 0,
    "status": 404,
    "type": "yii\\web\\NotFoundHttpException"
}

13. RESTful APIs are typically stateless. Therefore, configure the user application component by editing \api\config\main.php.
Note: Configure the user application component:
Set the [[yii\web\User::enableSession|enableSession]] property to false.
Set the [[yii\web\User::loginUrl|loginUrl]] property to null to display an HTTP 403 error instead of redirecting to the login page.

PHP
        'user' => [
            'identityClass' => 'api\models\User',
            'enableSession' => false,
            'loginUrl' => null,
            'enableAutoLogin' => false,
        ],

14. Copy Action.php, IndexAction.php, OptionsAction.php, ViewAction.php, CreateAction.php, UpdateAction.php, DeleteAction.php, and Serializer.php from the \vendor\yiisoft\yii2\rest directory to the \api\rests\user directory. If the directory name consists of multiple words, it is recommended to use lowercase with underscores. Reference URL: https://github.com/hfcorriez/fig-standards/blob/master/accepted/zh_CN/PSR-0.md

15. Edit \api\rests\user\IndexAction.php to adjust the namespace, inheritance relationship, query conditions, etc.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
use yii\data\ActiveDataProvider;
 
/**
 * IndexAction implements the API endpoint for listing multiple models.
 *
 * For more details and usage information on IndexAction, see the [guide article on rest controllers](guide:rest-controllers).
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class IndexAction extends \yii\rest\IndexAction
{
    const STATUS_DELETED = 0; //状态:已删除
    const STATUS_ACTIVE = 10; //状态:活跃
 
    /**
     * Prepares the data provider that should return the requested collection of the models.
     * @return ActiveDataProvider
     */
    protected function prepareDataProvider()
    {
        $requestParams = Yii::$app->getRequest()->getBodyParams();
        if (empty($requestParams)) {
            $requestParams = Yii::$app->getRequest()->getQueryParams();
        }
 
        $filter = null;
        if ($this->dataFilter !== null) {
            $this->dataFilter = Yii::createObject($this->dataFilter);
            if ($this->dataFilter->load($requestParams)) {
                $filter = $this->dataFilter->build();
                if ($filter === false) {
                    return $this->dataFilter;
                }
            }
        }
 
        if ($this->prepareDataProvider !== null) {
            return call_user_func($this->prepareDataProvider, $this, $filter);
        }
 
        /* @var $modelClass \yii\db\BaseActiveRecord */
        $modelClass = $this->modelClass;
 
        $query = $modelClass::find()->where(['status' => self::STATUS_ACTIVE]);
        if (!empty($filter)) {
            $query->andWhere($filter);
        }
 
        return Yii::createObject([
            'class' => ActiveDataProvider::className(),
            'query' => $query,
            'pagination' => [
                'params' => $requestParams,
            ],
            'sort' => [
                'params' => $requestParams,
            ],
        ]);
    }
}
 

16. Edit \api\rests\user\Serializer.php to adjust the namespace, inheritance relationship, response structure (successful response: “code”: 10000, “message”, “data”; failed response: “code”: a number other than 10000, “message”), etc.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
use yii\data\DataProviderInterface;
 
/**
 * Serializer converts resource objects and collections into array representation.
 *
 * Serializer is mainly used by REST controllers to convert different objects into array representation
 * so that they can be further turned into different formats, such as JSON, XML, by response formatters.
 *
 * The default implementation handles resources as [[Model]] objects and collections as objects
 * implementing [[DataProviderInterface]]. You may override [[serialize()]] to handle more types.
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class Serializer extends \yii\rest\Serializer
{
    /**
     * Serializes a data provider.
     * @param DataProviderInterface $dataProvider
     * @return array the array representation of the data provider.
     */
    protected function serializeDataProvider($dataProvider)
    {
        if ($this->preserveKeys) {
            $models = $dataProvider->getModels();
        } else {
            $models = array_values($dataProvider->getModels());
        }
        $models = $this->serializeModels($models);
 
        if (($pagination = $dataProvider->getPagination()) !== false) {
            $this->addPaginationHeaders($pagination);
        }
 
        if ($this->request->getIsHead()) {
            return null;
        } elseif ($this->collectionEnvelope === null) {
            return $models;
        }
 
        $result = [
            $this->collectionEnvelope => $models,
        ];
 
        if (empty($result['items'])) {
            return ['code' => 20001, 'message' => Yii::t('error', '20001')];
        }
 
        if ($pagination !== false) {
            return ['code' => 10000, 'message' => Yii::t('success', '10001'), 'data' => array_merge($result, $this->serializePagination($pagination))];
        }
 
        return ['code' => 10000, 'message' => Yii::t('success', '10001'), 'data' => $result];
    }
}
 

17. Edit \api\config\main.php to configure the i18n application component for the API application.

PHP
        'i18n' => [
            'translations' => [
                'model/*'=> [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'forceTranslation' => true,
                    'basePath'=>'@common/messages',
                    'fileMap'=>[
                    ],
                ],
                '*'=> [
                    'class' => 'yii\i18n\PhpMessageSource',
                    'forceTranslation' => true,
                    'basePath'=>'@api/messages',
                    'fileMap'=>[
                    ],
                ],
            ],
        ],

18. Create a language pack file: \api\messages\zh-CN\success.php (Simplified Chinese, successful response).

PHP
<?php
return [
    10000 => 'success',
    10001 => '获取用户列表成功',
    10002 => '获取用户详情成功',
    10003 => '创建用户成功',
    10004 => '更新用户成功',
    10005 => '删除用户成功',
];

19. Create a language pack file: \api\messages\zh-CN\error.php (Simplified Chinese, failed response).

PHP
<?php
return [
    20000 => 'error',
    20001 => '用户列表为空',
    20002 => '用户ID:{id},不存在',
    20003 => '用户ID:{id},的状态为已删除',
    20004 => '数据验证失败:{firstErrors}',
];

20. Create a language pack file: \api\messages\en-US\success.php (US English, successful response).

PHP
<?php
return [
    10000 => 'success',
    10001 => 'Get user list success',
    10002 => 'Get user details success',
    10003 => 'Create user success',
    10004 => 'Update user success',
    10005 => 'Delete user success',
];
 

21. Create a language pack file: \api\messages\en-US\error.php (US English, failed response).

PHP
<?php
return [
    20000 => 'error',
    20001 => 'User list is empty',
    20002 => 'User ID: {id}, does not exist',
    20003 => 'User ID: {id}, the status is not active',
    20004 => 'Data validation failed: {firstErrors}',
];
 

22. ContentNegotiator supports response content format handling and language handling. It determines the response content format and language by inspecting the GET parameters and the Accept HTTP header. Configure ContentNegotiator to support US English and Simplified Chinese. Edit \common\config\main.php.

Note: If no language is detected in the request, the first configured item in [[languages]] is used.

PHP
    'bootstrap' => ['contentNegotiator'],
    'components' => [

        'contentNegotiator' => [
            'class' => 'yii\filters\ContentNegotiator',
            'languages' => [
                'zh-CN',
                'en-US',
            ],
        ],

    ],

Delete

PHP
    'sourceLanguage' => 'en-US',
    'language' => 'zh-CN',

23. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, with the message defaulting to Simplified Chinese, as shown in Figure 9.
Note:
Accept application/json; version=0.0

In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, with the message defaulting to Simplified Chinese
Figure 9
JSON
{
    "code": 10000,
    "message": "获取用户列表成功",
    "data": {
        "items": [
            {
                "id": 1,
                "username": "111111",
                "auth_key": "P66wcJVGQL2toEpYr-Kc3z_rk7jJHMip",
                "password_hash": "$2y$13$qWV4xv1YLBtyn5cVx106VO6kTUOUP/7kX3oaC6CVpczHobkUrc6AG",
                "password_reset_token": null,
                "email": "111111@163.com",
                "status": 10,
                "created_at": 1522821105,
                "updated_at": 1522821105
            },
            {
                "id": 2,
                "username": "222222",
                "auth_key": "MgGfnpw6mYtkzctYkTOscyJAY_xxu739",
                "password_hash": "$2y$13$Xe0I4yjXL1FLvue8Q1YEU.nX8wH9HZzBOHSdbx2FL49me15CqoSa2",
                "password_reset_token": null,
                "email": "222222@163.com",
                "status": 10,
                "created_at": 1522821319,
                "updated_at": 1522821319
            },
            {
                "id": 3,
                "username": "333333",
                "auth_key": "WPeS81Lh4EUg0dp8FQ7ely4MF1ucF-ph",
                "password_hash": "$2y$13$.2G63zy8q2wTH/dWf.gLVe7c8kXKw3YCT5gquVhNhpTS2Albro1Ua",
                "password_reset_token": null,
                "email": "333333@163.com",
                "status": 10,
                "created_at": 1522823787,
                "updated_at": 1522823787
            },
            {
                "id": 4,
                "username": "444444",
                "auth_key": "scn4skhTfkg9YvRH1q9JZHkkddSR9H6a",
                "password_hash": "$2y$13$0RNx33MZIwC.FlSzMB0fuOCJAKmb1TlZzWCAujaCY/ixiM11myQ0a",
                "password_reset_token": null,
                "email": "444444@163.com",
                "status": 10,
                "created_at": 1522823915,
                "updated_at": 1522823915
            }
        ],
        "_links": {
            "self": {
                "href": "http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users?user=1"
            }
        },
        "_meta": {
            "totalCount": 4,
            "userCount": 1,
            "currentuser": 1,
            "peruser": 20
        }
    }
}

24. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, with the message in Simplified Chinese, as shown in Figure 10.
Note:
Accept application/json; version=0.0
Accept-Language zh-CN

In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, with the message in Simplified Chinese
Figure 10
JSON
{
    "code": 10000,
    "message": "获取用户列表成功",
    "data": {
        "items": [
            {
                "id": 1,
                "username": "111111",
                "auth_key": "P66wcJVGQL2toEpYr-Kc3z_rk7jJHMip",
                "password_hash": "$2y$13$qWV4xv1YLBtyn5cVx106VO6kTUOUP/7kX3oaC6CVpczHobkUrc6AG",
                "password_reset_token": null,
                "email": "111111@163.com",
                "status": 10,
                "created_at": 1522821105,
                "updated_at": 1522821105
            },
            {
                "id": 2,
                "username": "222222",
                "auth_key": "MgGfnpw6mYtkzctYkTOscyJAY_xxu739",
                "password_hash": "$2y$13$Xe0I4yjXL1FLvue8Q1YEU.nX8wH9HZzBOHSdbx2FL49me15CqoSa2",
                "password_reset_token": null,
                "email": "222222@163.com",
                "status": 10,
                "created_at": 1522821319,
                "updated_at": 1522821319
            },
            {
                "id": 3,
                "username": "333333",
                "auth_key": "WPeS81Lh4EUg0dp8FQ7ely4MF1ucF-ph",
                "password_hash": "$2y$13$.2G63zy8q2wTH/dWf.gLVe7c8kXKw3YCT5gquVhNhpTS2Albro1Ua",
                "password_reset_token": null,
                "email": "333333@163.com",
                "status": 10,
                "created_at": 1522823787,
                "updated_at": 1522823787
            },
            {
                "id": 4,
                "username": "444444",
                "auth_key": "scn4skhTfkg9YvRH1q9JZHkkddSR9H6a",
                "password_hash": "$2y$13$0RNx33MZIwC.FlSzMB0fuOCJAKmb1TlZzWCAujaCY/ixiM11myQ0a",
                "password_reset_token": null,
                "email": "444444@163.com",
                "status": 10,
                "created_at": 1522823915,
                "updated_at": 1522823915
            }
        ],
        "_links": {
            "self": {
                "href": "http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users?user=1"
            }
        },
        "_meta": {
            "totalCount": 4,
            "userCount": 1,
            "currentuser": 1,
            "peruser": 20
        }
    }
}

25. GET /users/1: Returns the details of user 1. Edit \api\rests\user\Action.php to adjust the namespace, inheritance relationship, response structure, etc.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
use yii\db\ActiveRecordInterface;
use yii\web\NotFoundHttpException;
 
/**
 * Action is the base class for action classes that implement RESTful API.
 *
 * For more details and usage information on Action, see the [guide article on rest controllers](guide:rest-controllers).
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class Action extends \yii\rest\Action
{
    /**
     * Returns the data model based on the primary key given.
     * If the data model is not found, a 404 HTTP exception will be raised.
     * @param string $id the ID of the model to be loaded. If the model has a composite primary key,
     * the ID must be a string of the primary key values separated by commas.
     * The order of the primary key values should follow that returned by the `primaryKey()` method
     * of the model.
     * @return ActiveRecordInterface the model found
     * @throws NotFoundHttpException if the model cannot be found
     */
    public function findModel($id)
    {
        if ($this->findModel !== null) {
            return call_user_func($this->findModel, $id, $this);
        }
 
        /* @var $modelClass ActiveRecordInterface */
        $modelClass = $this->modelClass;
        $keys = $modelClass::primaryKey();
        if (count($keys) > 1) {
            $values = explode(',', $id);
            if (count($keys) === count($values)) {
                $model = $modelClass::findOne(array_combine($keys, $values));
            }
        } elseif ($id !== null) {
            $model = $modelClass::findOne($id);
        }
 
        if (isset($model)) {
            return $model;
        }
 
        throw new NotFoundHttpException(Yii::t('error', Yii::t('error', Yii::t('error', '20002'), ['id' => $id])), 20002);
    }
}

26. Edit \api\rests\user\ViewAction.php to adjust the namespace, inheritance relationship, response structure, etc.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
 
/**
 * ViewAction implements the API endpoint for returning the detailed information about a model.
 *
 * For more details and usage information on ViewAction, see the [guide article on rest controllers](guide:rest-controllers).
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class ViewAction extends Action
{
    const STATUS_DELETED = 0; //状态:已删除
    const STATUS_ACTIVE = 10; //状态:活跃
 
    /**
     * Displays a model.
     * @param string $id the primary key of the model.
     * @return \yii\db\ActiveRecordInterface the model being displayed
     */
    public function run($id)
    {
        $model = $this->findModel($id);
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id, $model);
        }
 
        /* 判断状态,如果为已删除,则返回失败 */
        if ($model->status === self::STATUS_DELETED) {
            return ['code' => 20003, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20003'), ['id' => $id]))];
        }
 
        return ['code' => 10000, 'message' => Yii::t('success', '10002'), 'data' => $model];
    }
}
 
 

27. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 returns a 200 response, and its status is active, as shown in Figure 11.
Note:
Accept application/json; version=0.0
Accept-Language zh-CN

In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 returns a 200 response, and its status is active
Figure 11
JSON
{
    "code": 10000,
    "message": "获取用户详情成功",
    "data": {
        "id": 1,
        "username": "111111",
        "auth_key": "P66wcJVGQL2toEpYr-Kc3z_rk7jJHMip",
        "password_hash": "$2y$13$qWV4xv1YLBtyn5cVx106VO6kTUOUP/7kX3oaC6CVpczHobkUrc6AG",
        "password_reset_token": null,
        "email": "111111@163.com",
        "status": 10,
        "created_at": 1522821105,
        "updated_at": 1522821105
    }
}

28. In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 404 response, indicating the user does not exist, as shown in Figure 12.
Note:
Accept application/json; version=0.0
Accept-Language en-US

In Postman, GET http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 404 response, indicating the user does not exist
Figure 12
JSON
{
    "name": "Not Found",
    "message": "User ID: 5, does not exist",
    "code": 20002,
    "status": 404,
    "type": "yii\\web\\NotFoundHttpException"
}

29. POST /users: Create a new user. Copy \api\models\SignupForm.php to \api\models\Signup.php.

PHP
<?php
namespace api\models;
 
use yii\base\Model;
 
/**
 * Signup
 */
class Signup extends Model
{
    public $username;
    public $email;
    public $password;
 
 
    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['username', 'trim'],
            ['username', 'required'],
            ['username', 'unique', 'targetClass' => '\api\models\User'],
            ['username', 'string', 'min' => 2, 'max' => 255],
            ['email', 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            ['email', 'string', 'max' => 255],
            ['email', 'unique', 'targetClass' => '\api\models\User'],
            ['password', 'required'],
            ['password', 'string', 'min' => 6],
        ];
    }
}
 

30. Create \api\modules\v1\models\Signup.php, which inherits from \api\models\Signup.php.

PHP
<?php
/**
 * Created by PhpStorm.
 * User: WangQiang
 * Date: 2018/04/08
 * Time: 10:48
 */
 
namespace api\modules\v1\models;
 
 
class Signup extends \api\models\Signup
{
 
}

31. POST /users: Create a new user. Edit \api\rests\user\CreateAction.php.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
use yii\base\Model;
use api\models\Signup;
use yii\helpers\Url;
use yii\web\ServerErrorHttpException;
 
/**
 * CreateAction implements the API endpoint for creating a new model from the given data.
 *
 * For more details and usage information on CreateAction, see the [guide article on rest controllers](guide:rest-controllers).
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class CreateAction extends Action
{
    /**
     * @var string the scenario to be assigned to the new model before it is validated and saved.
     */
    public $scenario = Model::SCENARIO_DEFAULT;
    /**
     * @var string the name of the view action. This property is need to create the URL when the model is successfully created.
     */
    public $viewAction = 'view';
 
 
    /**
     * Creates a new model.
     * @return \yii\db\ActiveRecordInterface the model newly created
     * @throws ServerErrorHttpException if there is any error when creating the model
     */
    public function run()
    {
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id);
        }
 
        $signup = new Signup();
        $signup->load(Yii::$app->getRequest()->getBodyParams(), '');
        if (!$signup->validate()) {
            if ($signup->hasErrors()) {
                $response = Yii::$app->getResponse();
                $response->setStatusCode(422, 'Data Validation Failed.');
                foreach ($signup->getFirstErrors() as $message) {
                    $firstErrors = $message;
                    break;
                }
                return ['code' => 20004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20004'), ['firstErrors' => $firstErrors]))];
            } elseif (!$signup->hasErrors()) {
                throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
            }
        }
 
        /* @var $model \yii\db\ActiveRecord */
        $model = new $this->modelClass([
            'scenario' => $this->scenario,
        ]);
 
        $model->username = $signup->username;
        $model->email = $signup->email;
        $model->setPassword($signup->password);
        $model->generateAuthKey();
        if ($model->save()) {
            $response = Yii::$app->getResponse();
            $response->setStatusCode(201);
            $id = implode(',', array_values($model->getPrimaryKey(true)));
            $response->getHeaders()->set('Location', Url::toRoute([$this->viewAction, 'id' => $id], true));
        } elseif ($model->hasErrors()) {
            $response = Yii::$app->getResponse();
            $response->setStatusCode(422, 'Data Validation Failed.');
            foreach ($model->getFirstErrors() as $message) {
                $firstErrors = $message;
                break;
            }
            return ['code' => 20004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20004'), ['firstErrors' => $firstErrors]))];
        } elseif (!$model->hasErrors()) {
            throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
        }
 
        return ['code' => 10000, 'message' => Yii::t('success', '10003'), 'data' => $model];
    }
}

32. In Postman, POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 201 response, as shown in Figure 13.
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded

In Postman, POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 201 response
Figure 13
JSON
{
    "code": 10000,
    "message": "创建用户成功",
    "data": {
        "username": "555555",
        "email": "555555@163.com",
        "password_hash": "$2y$13$JcBRxuLFtLc4FfACRGwKpeXRNfhuP8XRGR/0ORvpXc8ob3c3D2E/q",
        "auth_key": "0eAriaA02Qw9J3s8ngwLtGlW34tpZ98k",
        "status": 10,
        "created_at": 1523156052,
        "updated_at": 1523156052,
        "id": 5
    }
}

33. In Postman, POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users with the parameters kept as is returns a 422 response, as shown in Figure 14.
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded

In Postman, POST http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users with the parameters kept as is returns a 422 response
Figure 14
JSON
{
    "code": 20004,
    "message": "数据验证失败:Username的值\"555555\"已经被占用了。"
}

34. PUT /users/5: Update a user. Create \api\models\UserUpdate.php.

PHP
<?php
namespace api\models;
 
use yii\base\Model;
 
/**
 * UserUpdate
 */
class UserUpdate extends Model
{
    public $id;
    public $email;
    public $password;
    public $status;
 
    const STATUS_DELETED = 0; //状态:已删除
    const STATUS_ACTIVE = 10; //状态:活跃
 
    /**
     * {@inheritdoc}
     */
    public function rules()
    {
        return [
            ['email', 'trim'],
            ['email', 'required'],
            ['email', 'email'],
            ['email', 'string', 'max' => 255],
            ['email', 'unique', 'targetClass' => '\api\models\User', 'filter' => ['!=', 'id', $this->id]],
            ['password', 'required'],
            ['password', 'string', 'min' => 6],
            [['status'], 'in', 'range' => [self::STATUS_DELETED, self::STATUS_ACTIVE]],
        ];
    }
}

35. Create \api\modules\v1\models\UserUpdate.php, which inherits from \api\models\UserUpdate.php.

PHP
<?php
/**
 * Created by PhpStorm.
 * User: WangQiang
 * Date: 2018/04/08
 * Time: 11:19
 */
 
namespace api\modules\v1\models;
 
 
class UserUpdate extends \api\models\UserUpdate
{
 
}

36. PUT /users/4: Update a user. Edit \api\rests\user\UpdateAction.php.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
use yii\base\Model;
use yii\db\ActiveRecord;
use api\models\UserUpdate;
use yii\web\ServerErrorHttpException;
 
/**
 * UpdateAction implements the API endpoint for updating a model.
 *
 * For more details and usage information on UpdateAction, see the [guide article on rest controllers](guide:rest-controllers).
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class UpdateAction extends Action
{
    /**
     * @var string the scenario to be assigned to the model before it is validated and updated.
     */
    public $scenario = Model::SCENARIO_DEFAULT;
 
 
    /**
     * Updates an existing model.
     * @param string $id the primary key of the model.
     * @return \yii\db\ActiveRecordInterface the model being updated
     * @throws ServerErrorHttpException if there is any error when updating the model
     */
    public function run($id)
    {
        /* @var $model ActiveRecord */
        $model = $this->findModel($id);
 
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id, $model);
        }
 
        $userUpdate = new UserUpdate();
        $userUpdate->id = $id;
        $userUpdate->load(Yii::$app->getRequest()->getBodyParams(), '');
        if (!$userUpdate->validate()) {
            if ($userUpdate->hasErrors()) {
                $response = Yii::$app->getResponse();
                $response->setStatusCode(422, 'Data Validation Failed.');
                foreach ($userUpdate->getFirstErrors() as $message) {
                    $firstErrors = $message;
                    break;
                }
                return ['code' => 20004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20004'), ['firstErrors' => $firstErrors]))];
            } elseif (!$userUpdate->hasErrors()) {
                throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
            }
        }
 
        $model->scenario = $this->scenario;
        $model->email = $userUpdate->email;
        $model->status = $userUpdate->status;
        $model->setPassword($userUpdate->password);
        $model->generateAuthKey();
        if ($model->save() === false) {
            if ($model->hasErrors()) {
                $response = Yii::$app->getResponse();
                $response->setStatusCode(422, 'Data Validation Failed.');
                foreach ($model->getFirstErrors() as $message) {
                    $firstErrors = $message;
                    break;
                }
                return ['code' => 20004, 'message' => Yii::t('error', Yii::t('error', Yii::t('error', '20004'), ['firstErrors' => $firstErrors]))];
            } elseif (!$model->hasErrors()) {
                throw new ServerErrorHttpException('Failed to update the object for unknown reason.');
            }
        }
 
        return ['code' => 10000, 'message' => Yii::t('success', '10004'), 'data' => $model];
    }
}
 

37. In Postman, PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 200 response, as shown in Figure 15.

In Postman, PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 200 response
Figure 15
JSON
{
    "code": 10000,
    "message": "更新用户成功",
    "data": {
        "id": 5,
        "username": "555555",
        "auth_key": "nXryM-o_ky4TAdn758l47pQhLhsSnjSZ",
        "password_hash": "$2y$13$qB5Y3Zl7B8NyPDFMXc06ye8OAhybCpp7p6wToyhtLH2iDzhd06ts.",
        "password_reset_token": null,
        "email": "555555@qq.com",
        "status": "0",
        "created_at": 1523156052,
        "updated_at": 1523165956
    }
}

38. In Postman, PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/4 returns a 422 response, as shown in Figure 16.

Note:
Accept application/json; version=0.0
Accept-Language en-US
Accept-Encoding gzip, deflate, br
Content-Type application/x-www-form-urlencoded

Body
The value of email has already been taken by another user (if the value remains unchanged, validation passes).

In Postman, PUT http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/4 returns a 422 response
Figure 16
JSON
{
    "code": 20004,
    "message": "Data validation failed: Email \"444444@163.com\" has already been taken."
}

39. DELETE /users/5: Delete user 5. Edit \api\rests\user\DeleteAction.php.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
use yii\web\ServerErrorHttpException;
 
/**
 * DeleteAction implements the API endpoint for deleting a model.
 *
 * For more details and usage information on DeleteAction, see the [guide article on rest controllers](guide:rest-controllers).
 *
 * @author Qiang Wang <shuijingwanwq@163.com>
 * @since 1.0
 */
class DeleteAction extends Action
{
    /**
     * Deletes a model.
     * @param mixed $id id of the model to be deleted.
     * @throws ServerErrorHttpException on failure.
     */
    public function run($id)
    {
        $model = $this->findModel($id);
 
        if ($this->checkAccess) {
            call_user_func($this->checkAccess, $this->id, $model);
        }
 
        if ($model->delete() === false) {
            throw new ServerErrorHttpException('Failed to delete the object for unknown reason.');
        }
 
        return ['code' => 10000, 'message' => Yii::t('success', '10005')];
    }
}
 
 

40. In Postman, DELETE http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 200 response, as shown in Figure 17.
Note:
Accept application/json; version=0.0
Accept-Language zh-CN
Accept-Encoding gzip, deflate, br

In Postman, DELETE http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/5 returns a 200 response
Figure 17
JSON
{
    "code": 10000,
    "message": "删除用户成功"
}

41. Edit \api\rests\user\OptionsAction.php.

PHP
<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
 
namespace api\rests\user;
 
use Yii;
 
/**
 * OptionsAction responds to the OPTIONS request by sending back an `Allow` header.
 *
 * For more details and usage information on OptionsAction, see the [guide article on rest controllers](guide:rest-controllers).
 *
 * @author Qiang Xue <qiang.xue@gmail.com>
 * @since 2.0
 */
class OptionsAction extends \yii\rest\Action
{
    /**
     * @var array the HTTP verbs that are supported by the collection URL
     */
    public $collectionOptions = ['GET', 'POST', 'HEAD', 'OPTIONS'];
    /**
     * @var array the HTTP verbs that are supported by the resource URL
     */
    public $resourceOptions = ['GET', 'PUT', 'PATCH', 'DELETE', 'HEAD', 'OPTIONS'];
 
 
    /**
     * Responds to the OPTIONS request.
     * @param string $id
     */
    public function run($id = null)
    {
        if (Yii::$app->getRequest()->getMethod() !== 'OPTIONS') {
            Yii::$app->getResponse()->setStatusCode(405);
        }
        $options = $id === null ? $this->collectionOptions : $this->resourceOptions;
        $headers = Yii::$app->getResponse()->getHeaders();
        $headers->set('Allow', implode(', ', $options));
        $headers->set('Access-Control-Allow-Methods', implode(', ', $options));
    }
}
 

42. OPTIONS /users: Shows the verbs supported by the endpoint /users. In Postman, OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response, as shown in Figure 18.

OPTIONS /users: Shows the verbs supported by the endpoint /users. In Postman, OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users returns a 200 response
Figure 18

43. OPTIONS /users/4: Shows the verbs supported by the endpoint /users/4. In Postman, OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 returns a 200 response, as shown in Figure 19.

OPTIONS /users/4: Shows the verbs supported by the endpoint /users/4. In Postman, OPTIONS http://api.github-shuijingwan-yii2-app-advanced.localhost/v1/users/1 returns a 200 response
Figure 19

44. Since the [[yii\web\Response::format|response format]] is not html, error or exception views will not be used to display error messages. The error action can be removed. Edit \api\config\main.php.

Delete

PHP
        'errorHandler' => [
            'errorAction' => 'site/error',
        ],

45. Summary: The currently supported actions are: index, view, create, update, delete, and options. All of them are implemented by overriding the inherited methods to meet specific requirements.

系列导航

关于作者 · 持续学习与技术实践

我是一名拥有 15 年以上开发经验的技术从业者,自 2013 年起持续运营个人技术博客,记录工作与个人项目中遇到的真实问题,以及解决问题过程中的探索、实践和思考。

除了技术写作,我也在持续开发和维护自己的独立项目,探索新技术和新工具在实际场景中的应用。我始终相信,没有不值得去解决的问题,也没有不值得去学习的技术。

如果这篇文章对你有所帮助,欢迎浏览博客中的其他文章,也欢迎交流技术经验与想法。

关于我  ·  GitHub  ·  邮件联系