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

Implementing Batch Resource Creation via POST APIs in Yii 2.0 RESTful Web Services

In Postman, the request parameters

1. \api\rests\plan_task\CreateAction.php. For the specific implementation, refer to: https://github.com/yiisoft/yii2/blob/master/docs/guide-zh-CN/input-tabular-input.md . The code is as follows:

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;
 
/**
 * 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. In Postman, the request parameters are shown in Figure 1.

Plaintext
0[plan_id]:1
0[title]:Task 7 under the title of Morning News 1
0[config_task_id]:2
0[exec_user_id]:8
0[place]:Location 7 under the title of Morning News 1
0[task_info]:Content 7 under the title of Morning News 1
1[plan_id]:1
1[title]:Task 8 under the title of Morning News 1
1[config_task_id]:2
1[exec_user_id]:8
1[place]:Location 8 under the title of Morning News 1
1[task_info]:Location 8 under the title of Morning News 1

In Postman, the request parameters
Figure 1

3. At the frontend’s request, a form name was added. \api\rests\plan_task\CreateAction.php. The code is as follows:

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. The code is as follows:

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. In Postman, the request parameters are shown in Figure 2.

Plaintext
plan_task[0][plan_id]:2
plan_task[0][title]:Title of Task 6 of Topic 2
plan_task[0][config_task_id]:2
plan_task[0][exec_user_id]:187
plan_task[0][place]:Location of Task 6 of Topic 2
plan_task[0][task_info]:Content of Task 6 of Topic 2
plan_task[1][plan_id]:2
plan_task[1][title]:Title of Task 7 of Topic 2
plan_task[1][config_task_id]:2
plan_task[1][exec_user_id]:191
plan_task[1][place]:Location of Task 7 of Topic 2
plan_task[1][task_info]:Content of Task 7 of Topic 2

In Postman, the request parameters
Figure 2

6. In Postman, the response parameters are shown in Figure 3.

JSON

{
    "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
            }
        ]
    }
}

In Postman, the response parameters
Figure 3

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

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

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

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

关于我  ·  GitHub  ·  邮件联系