在 Yii 2.0 上,RESTful 风格的 Web Service 服务的 API,PUT 批量更新资源的实现
1、编辑 \api\config\urlManager.php,定义路由以支持 PUT close/{id}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // 任务管理 [ 'class' => 'yii\rest\UrlRule' , 'controller' => [ 'v1/plan-task' ], 'only' => [ 'index' , 'create' , 'view' , 'update' , 'claim' , 'delete' , 'finish' , 'transfer' , 'video-edit' , 'write' , 'commit-article' , 'article-review' , 'close' ], 'tokens' => [ '{id}' => '<id:\\w[\\w,:;]*>' ], 'extraPatterns' => [ 'PUT claim/{id}' => 'claim' , 'DELETE /{id}' => '' , 'PUT finish/{id}' => 'finish' , 'GET {id}' => 'view' , 'PUT update' => 'update' , 'PUT transfer/{id}' => 'transfer' , 'GET video-edit/{id}' => 'video-edit' , 'GET write/{id}' => 'write' , 'GET commit-article/{id}' => 'commit-article' , 'GET article-review/{id}' => 'article-review' , 'PUT close/{id}' => 'close' , ], ], |
2、编辑 \api\controllers\PlanTaskController.php,定义动作以支持 close 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | <?php /** * Created by PhpStorm. * User: WangQiang * Date: 2018/05/05 * Time: 11:43 */ namespace api\controllers; use yii\rest\ActiveController; class PlanTaskController extends ActiveController { public $serializer = [ 'class' => 'api\rests\plan_task\Serializer' , 'collectionEnvelope' => 'items' , ]; /** * @inheritdoc */ public function actions() { $actions = parent::actions(); // 禁用"options"动作 unset( $actions [ 'options' ]); $actions [ 'index' ][ 'class' ] = 'api\rests\plan_task\IndexAction' ; $actions [ 'create' ][ 'class' ] = 'api\rests\plan_task\CreateAction' ; $actions [ 'view' ][ 'class' ] = 'api\rests\plan_task\ViewAction' ; $actions [ 'update' ][ 'class' ] = 'api\rests\plan_task\UpdateAction' ; $actions [ 'delete' ][ 'class' ] = 'api\rests\plan_task\DeleteAction' ; $actions [ 'claim' ] = [ 'class' => 'api\rests\plan_task\ClaimAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; $actions [ 'finish' ] = [ 'class' => 'api\rests\plan_task\FinishAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; $actions [ 'transfer' ] = [ 'class' => 'api\rests\plan_task\TransferAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; $actions [ 'video-edit' ] = [ 'class' => 'api\rests\plan_task\VideoEditAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; $actions [ 'write' ] = [ 'class' => 'api\rests\plan_task\WriteAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; $actions [ 'commit-article' ] = [ 'class' => 'api\rests\plan_task\CommitArticleAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; $actions [ 'article-review' ] = [ 'class' => 'api\rests\plan_task\ArticleReviewAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; $actions [ 'close' ] = [ 'class' => 'api\rests\plan_task\CloseAction' , 'modelClass' => $this ->modelClass, 'checkAccess' => [ $this , 'checkAccess' ], ]; return $actions ; } } |
3、编辑 \api\rests\plan_task\Action.php,复制 public function findModel($id) 为 public function findModels($id),以支持查找多个模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /** * 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 findModels( $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 ::find()->where([ 'in' , $keys , $values ])->indexBy( $keys )->all(); } } elseif ( $id !== null) { $values = explode ( ';' , $id ); $model = $modelClass ::find()->where([ 'in' , 'id' , $values ])->indexBy( 'id' )->all(); } if (! empty ( $model )) { // ID的数目与模型资源数目是否相等,如果不相等,响应失败 $flipValues = array_flip ( $values ); if ( count ( $model ) == count ( $flipValues )) { return $model ; } else { $id = implode( ";" , array_keys ( array_diff_key ( $flipValues , $model ))); } } throw new NotFoundHttpException(Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20014' ), [ 'id' => $id ])), 20014); } |
4、所有ID皆必须存在,否则响应失败,如图1
5、新建 \api\rests\plan_task\CloseAction.php,支持 批量验证模型、批量更新模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | <?php namespace api\rests\plan_task; use Yii; use yii\base\Model; use yii\db\ActiveRecord; use api\models\PlanTask; use yii\web\ServerErrorHttpException; /** * 关闭(关闭任务) * * 1、请求参数列表 * (1)ids 必填,多个ID(以半角;分隔); * * 2、输入数据验证规则 * (1)必填:ids; * (2)所有选题任务ID皆必须存在; * (3)所有选题任务状态皆必须为,1:未开始;2:进行中(已认领); * (4)选题创建用户ID为当前登录用户ID || (栏目人员配置租户ID为当前登录用户租户ID && 栏目人员配置角色标识包含栏目负责人标识 && 栏目人员配置状态的非取值范围 -1、0); * * 3、操作数据(事务) * (1)更新选题任务的状态,4:关闭; * * For more details and usage information on CloseAction, see the [guide article on rest controllers](guide:rest-controllers). * * @author Qiang Wang <shuijingwanwq@163.com> * @since 1.0 */ class CloseAction extends Action { /** * 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 */ $models = $this ->findModels( $id ); if ( $this ->checkAccess) { call_user_func( $this ->checkAccess, $this ->id, $models ); } // 状态,不是 1:未开始;2:进行中(已认领) 的ID $ids = []; foreach ( $models as $key => $model ) { $model ->scenario = 'close' ; $models [ $key ] = $model ; if ( $model ->status != PlanTask::PLAN_TASK_STATUS_NOT_BEGINNING && $model ->status != PlanTask::PLAN_TASK_STATUS_BEGINNING) { $ids [] = $model ->id; } } if (! empty ( $ids )) { $ids = implode( ";" , $ids ); return [ 'code' => 20036, 'message' => Yii::t( 'error' , Yii::t( 'error' , Yii::t( 'error' , '20036' ), [ 'ids' => $ids ]))]; } // 批量验证模型 if (Model::validateMultiple( $models )) { $modelResult = $model ->closeMultiple( $models ); if (! $modelResult ) { return [ 'code' => 20038, 'message' => Yii::t( 'error' , '20038' )]; } } 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 update 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' , '10017' ), 'data' => [ 'items' => array_values ( $models )]]; } } |
6、编辑 \common\logics\PlanTask.php,支持 验证规则定义、批量更新模型
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 | /** * @inheritdoc */ public function rules() { $rules = [ /* 关闭(关闭任务) */ [[ 'id' ], 'validateClosePermission' , 'on' => 'close' ], //close ]; $parentRules = parent::rules(); return ArrayHelper::merge( $rules , $parentRules ); } /** * Validates the close permission. * This method serves as the inline validation for close permission. * * @param string $attribute the attribute currently being validated * @param array $params the additional name-value pairs given in the rule */ public function validateClosePermission( $attribute , $params ) { if (! $this ->hasErrors()) { // 当前用户的身份实例,未认证用户则为 Null $identity = Yii:: $app ->user->identity; if (! $this ->checkColumnOrPlanOwner( $identity ->group_id, $identity ->login_name)) { $this ->addError( $attribute , Yii::t( 'error' , '20037' )); } } } /** * 批量关闭(关闭任务) * * @param $models * * @return bool the saved model or false if saving fails * @throws \Throwable */ public function closeMultiple( $models ) { $transaction = Yii:: $app ->db->beginTransaction(); try { foreach ( $models as $model ) { $model ->status = self::PLAN_TASK_STATUS_CLOSE; if (! $model ->save(false)) { throw new ServerErrorHttpException(Yii::t( 'error' , '20038' ), 20038); } } $transaction ->commit(); return true; } catch (\Throwable $e ) { $transaction ->rollBack(); throw $e ; } } |
7、执行结果成功,批量更新模型成功,响应成功更新后的模型数据,如图2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 | { "code": 10000, "message": "关闭(关闭任务)成功", "data": { "items": [ { "id": 14, "group_id": "015ce30b116ce86058fa6ab4fea4ac63", "config_column_id": 1, "plan_id": 1, "sort_order": 1, "title": "无线成都1的任务2", "config_task_id": 1, "create_user_id": 8, "create_name": "13281105967", "exec_user_id": 186, "exec_name": "test2", "place": "<p>选题摘要</p>", "task_info": "", "task_data": [], "ended_at": 1530696654, "current_step_id": 0, "status": 4, "created_at": 1528104654, "updated_at": 1528270437 }, { "id": 15, "group_id": "015ce30b116ce86058fa6ab4fea4ac63", "config_column_id": 1, "plan_id": 1, "sort_order": 1, "title": "无线成都1的任务1", "config_task_id": 1, "create_user_id": 8, "create_name": "13281105967", "exec_user_id": 185, "exec_name": "test1", "place": "地点", "task_info": "<p>选题摘要</p>", "task_data": [], "ended_at": 1530754303, "current_step_id": 90, "status": 4, "created_at": 1528162303, "updated_at": 1528270437 } ] } } |
近期评论