在 Yii 2.0 上,RESTful 风格的 Web Service 服务的 API,POST 批量新建资源的实现

1、\api\rests\plan_task\CreateAction.php,具体实现可参考网址:https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/input-tabular-input.md ,代码如下:

<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
namespace api\rests\plan_task;

use Yii;
use yii\base\Model;
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;


    /**
     * 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);
        }

        // 当前用户的身份实例,未认证用户则为 Null
        $identity = Yii::$app->user->identity;

        /* @var $model \yii\db\ActiveRecord */        /*
        $model = new $this->modelClass([
            'scenario' => 'create',
        ]);
        */
        $requestParams = Yii::$app->getRequest()->getBodyParams();

        $count = count($requestParams);
        //创建一个初始的 $models 数组包含一个默认的模型
        $scenario = 'create';
        $models = [new $this->modelClass([
            'scenario' => $scenario,
        ])];
        for($i = 1; $i < $count; $i++) {
            $models[] = new $this->modelClass([
                'scenario' => $scenario,
            ]);
        }

        // 批量填充模型属性
        if (!Model::loadMultiple($models, $requestParams, '')) {
            return ['code' => 20010, 'message' => Yii::t('error', '20010')];
        }

        foreach ($models as $key => $model) {
            $model->group_id = $identity->group_id;
            $model->config_column_id = $model->plan['config_column_id'];
            $model->create_user_id = $identity->id;
            $model->create_name = $identity->login_name;
            $model->exec_name = $model->userByExecUserId['login_name'];
            $model->task_data = serialize([]);
            $model->ended_at = time() + Yii::$app->params['planTaskExpire'];
            $models[$key] = $model;
        }

        // 批量验证模型
        if (Model::validateMultiple($models)) {
            foreach ($models as $model) {
                $model->save(false);
            }
            $response = Yii::$app->getResponse();
            $response->setStatusCode(201);
        } else {
            return ['code' => 20011, 'message' => Yii::t('error', '20011')];
        }

        return ['code' => 10000, 'message' => Yii::t('success', '10006'), 'data' => ['items' => $models]];
    }
}

2、在 Postman 上,请求参数如图1

0[plan_id]:1
0[title]:早间新闻的标题1下的任务7
0[config_task_id]:2
0[exec_user_id]:8
0[place]:早间新闻的标题1下的地点7
0[task_info]:早间新闻的标题1下的内容7
1[plan_id]:1
1[title]:早间新闻的标题1下的任务8
1[config_task_id]:2
1[exec_user_id]:8
1[place]:早间新闻的标题1下的地点8
1[task_info]:早间新闻的标题1下的地点8

图1

3、应前端的请求,添加表单名称,\api\rests\plan_task\CreateAction.php,代码如下

<?php
/**
 * @link http://www.yiiframework.com/
 * @copyright Copyright (c) 2008 Yii Software LLC
 * @license http://www.yiiframework.com/license/
 */
namespace api\rests\plan_task;

use Yii;
use yii\base\Model;
use yii\helpers\Url;
use yii\web\ServerErrorHttpException;

/**
 * 指派(创建任务)
 *
 * 1、请求参数列表
 * (1)plan_id 必填,选题ID
 * (2)title 必填,任务标题
 * (3)config_task_id 必填,任务配置(类型)ID
 * (4)exec_user_id 必填,执行用户ID
 * (5)place 必填,地点
 * (6)task_info 必填,任务信息
 *
 * 2、输入数据验证规则
 * (1)必填:plan_id, title, config_task_id, exec_user_id, place, task_info;
 * (2)存在性:config_task_id 必须存在于任务配置模型中,且其状态为 1:启用;
 * (3)存在性:plan_id 必须存在于选题模型中,且其状态为 3:通过/5:指派;
 * (4)存在性:plan_id 选题所属栏目ID必须存在,且其状态为 1:启用;
 * (5)存在性:create_user_id 必须存在于选题所属栏目的人员配置模型中,且其状态为 1:启用;
 * (6)存在性:exec_user_id 必须存在于选题所属栏目的人员配置模型中,且其状态为 1:启用;
 * (7)默认值(''):'place', 'task_info';
 *
 * 3、操作数据(事务)
 * (1)循环插入任务(传递 false 作为 [[save()]]的一个参数使其不会重复验证两次);
 * (2)批量插入任务步骤,创建批量 INSERT 命令(batchInsert);
 * (3)更新选题状态:指派;
 *
 * 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;


    /**
     * 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);
        }

        // 当前用户的身份实例,未认证用户则为 Null
        $identity = Yii::$app->user->identity;

        /* @var $model \yii\db\ActiveRecord */        /*
        $model = new $this->modelClass([
            'scenario' => 'create',
        ]);
        */
        $requestParams = Yii::$app->getRequest()->getBodyParams();

        $formName = 'plan_task';
        $count = count($requestParams[$formName]);
        //创建一个初始的 $models 数组包含一个默认的模型
        $scenario = 'create';
        $models = [new $this->modelClass([
            'scenario' => $scenario,
        ])];
        for($i = 1; $i < $count; $i++) {
            $models[] = new $this->modelClass([
                'scenario' => $scenario,
            ]);
        }

        // 批量填充模型属性
        if (!Model::loadMultiple($models, $requestParams, $formName)) {
            return ['code' => 20010, 'message' => Yii::t('error', '20010')];
        }

        foreach ($models as $key => $model) {
            $model->group_id = $identity->group_id;
            $model->config_column_id = $model->planById['config_column_id'];
            $model->create_user_id = $identity->id;
            $model->create_name = $identity->login_name;
            $model->exec_name = $model->userByExecUserId['login_name'];
            $model->task_data = serialize([]);
            $model->ended_at = time() + Yii::$app->params['planTaskExpire'];
            $models[$key] = $model;
        }

        // 批量验证模型
        if (Model::validateMultiple($models)) {
            $modelResult = $model->createMultiple($models);
            if (!$modelResult) {
                return ['code' => 20015, 'message' => Yii::t('error', '20015')];
            }
            $response = Yii::$app->getResponse();
            $response->setStatusCode(201);
        } else {
            foreach ($models as $model) {
                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]))];
                }
            }

            throw new ServerErrorHttpException('Failed to create the object for unknown reason.');
        }

        foreach ($models as $key => $model) {
            $model->task_data = unserialize($model->task_data);
            $models[$key] = $model;
        }

        return ['code' => 10000, 'message' => Yii::t('success', '10006'), 'data' => ['items' => $models]];
    }
}

4、\common\logics\PlanTask.php,代码如下

    /**
     * 批量指派(创建任务)
     *
     * @param $models
     *
     * @return bool the saved model or false if saving fails
     * @throws \Throwable
     */    public function createMultiple($models)
    {
        $transaction = Yii::$app->db->beginTransaction();
        $time = time();
        try {
            foreach ($models as $model) {
                if (!$model->save(false)) {
                    throw new ServerErrorHttpException(Yii::t('error', '20015'), 20015);
                }
                
                /* 创建MySQL模型(选题任务步骤) */                $table = PlanTaskStep::tableName();
                $columns = [ 'group_id', 'task_id', 'task_title', 'step_code', 'step_name', 'sort_order', 'updated_name', 'created_at', 'updated_at' ];
                /* 将模型记录转换为索引数组 */                foreach ($model->configTaskSteps as $configTaskStep) {
                    if ($configTaskStep->status == ConfigTaskStep::CONFIG_TASK_STEP_STATUS_ENABLE && $configTaskStep->is_default == 1) {
                        $rows[] = [
                            $model->group_id,
                            $model->id,
                            $model->title,
                            $configTaskStep->step_code,
                            $configTaskStep->step_name,
                            $configTaskStep->sort_order,
                            $model->create_name,
                            $time,
                            $time,
                        ];
                    }
                }
            }
            Yii::$app->db->createCommand()->batchInsert($table, $columns, $rows)->execute();
            $transaction->commit();
            
            return true;
        } catch (\Throwable $e) {
            $transaction->rollBack();
            throw $e;
        }
    }

5、在 Postman 上,请求参数如图2

plan_task[0][plan_id]:2
plan_task[0][title]:选题2的任务6的标题
plan_task[0][config_task_id]:2
plan_task[0][exec_user_id]:187
plan_task[0][place]:选题2的任务6的地点
plan_task[0][task_info]:选题2的任务6的内容
plan_task[1][plan_id]:2
plan_task[1][title]:选题2的任务7的标题
plan_task[1][config_task_id]:2
plan_task[1][exec_user_id]:191
plan_task[1][place]:选题2的任务7的地点
plan_task[1][task_info]:选题2的任务7的内容

图2

6、在 Postman 上,响应参数如图3

{
    "code": 10000,
    "message": "指派(创建任务)成功",
    "data": {
        "items": [
            {
                "plan_id": "2",
                "title": "选题2的任务6的标题",
                "config_task_id": "2",
                "exec_user_id": "187",
                "place": "选题2的任务6的地点",
                "task_info": "选题2的任务6的内容",
                "group_id": "015ce30b116ce86058fa6ab4fea4ac63",
                "config_column_id": 1,
                "create_user_id": 8,
                "create_name": "13281105967",
                "exec_name": "test3",
                "task_data": "a:0:{}",
                "ended_at": 1529047691,
                "created_at": 1526455691,
                "updated_at": 1526455691,
                "id": 37
            },
            {
                "plan_id": "2",
                "title": "选题2的任务7的标题",
                "config_task_id": "2",
                "exec_user_id": "191",
                "place": "选题2的任务7的地点",
                "task_info": "选题2的任务7的内容",
                "group_id": "015ce30b116ce86058fa6ab4fea4ac63",
                "config_column_id": 1,
                "create_user_id": 8,
                "create_name": "13281105967",
                "exec_name": "test4",
                "task_data": "a:0:{}",
                "ended_at": 1529047691,
                "created_at": 1526455692,
                "updated_at": 1526455692,
                "id": 38
            }
        ]
    }
}

图3

 

永夜