-
Notifications
You must be signed in to change notification settings - Fork 1
/
BatchAction.php
206 lines (183 loc) · 6.33 KB
/
BatchAction.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
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
<?php
/**
* @link https://github.com/devzyj/yii2-rest
* @copyright Copyright (c) 2018 Zhang Yan Jiong
* @license http://opensource.org/licenses/BSD-3-Clause
*/
namespace devzyj\rest;
use Yii;
use yii\web\HttpException;
use yii\web\ForbiddenHttpException;
/**
* BatchAction 是实现 RESTful API 的批量动作类的基类。
*
* @author ZhangYanJiong <[email protected]>
* @since 1.0
*/
class BatchAction extends Action
{
use BatchActionTrait;
/**
* @event ActionEvent 在处理完模型列表后触发的事件。
*/
const EVENT_AFTER_PROCESS_MODELS = 'afterProcessModels';
/**
* @var integer 允许批量执行的资源个数。
*/
public $allowedCount;
/**
* @var string 批量操作请求资源过多的错误信息。
* 支持变量 `{allowedCount}` 和 `{requestedCount}`。
*/
public $manyResourcesMessage;
/**
* @var integer 批量操作请求资源过多的错误编码。
*/
public $manyResourcesCode;
/**
* @var string 多个ID时使用的分隔符。
*/
public $idsSeparator;
/**
* @var callable 根据多个主键,获取多个模型的回调方法。
* 方法应该只返回存在的数据,如果没有查询到数据,则返回空数组。
* 如果没有设置,则使用 [[findModels()]]。
*
* ```php
* function (array $ids, BatchAction $action) {
* // $ids 主键列表。
* // $action 当前正在执行的动作。
* }
* ```
*/
public $findModels;
/**
* {@inheritdoc}
*/
public function init()
{
if ($this->manyResourcesMessage === null) {
$this->manyResourcesMessage = 'The number of resources requested cannot exceed `{allowedCount}`.';
}
if ($this->manyResourcesCode === null) {
$this->manyResourcesCode = 0;
}
if ($this->idsSeparator === null) {
$this->idsSeparator = ';';
}
parent::init();
}
/**
* 检查允许批量执行的资源个数。
*
* @param array $data 请求的资源数组。
* @throws \yii\web\HttpException 如果设置了 [[$allowedCount]] 并且超出设置的数量。
*/
public function checkAllowedCount($data)
{
$requestedCount = count($data);
if ($this->allowedCount !== null && $requestedCount > $this->allowedCount) {
$manyResourcesMessage = strtr($this->manyResourcesMessage, [
'{allowedCount}' => $this->allowedCount,
'{requestedCount}' => $requestedCount,
]);
throw new HttpException(413, $manyResourcesMessage, $this->manyResourcesCode);
}
}
/**
* 根据指定的多个主键,返回多个数据模型。
*
* @param array $ids 主键列表。如果是复合主键,则使用逗号分隔,主键值的顺序应该遵循模型的 `primaryKey()` 方法返回的值。
* @return BatchModels 查询到的模型列表。返回的结果中只包含存在的数据,如果没有查询到数据,则返回空数组。
*/
public function findModels($ids)
{
$models = [];
if ($this->findModels) {
$models = call_user_func($this->findModels, $ids, $this);
} elseif ($ids && is_array($ids)) {
/* @var $modelClass \yii\db\ActiveRecordInterface */
$modelClass = $this->modelClass;
$keys = $modelClass::primaryKey();
if (count($keys) > 1) {
// composite primary key.
$condition = [];
foreach ($ids as $id) {
$values = explode($this->idSeparator, $id);
if (count($keys) === count($values)) {
$condition[] = array_combine($keys, $values);
}
}
if ($condition) {
array_unshift($condition, 'OR');
$models = $modelClass::find()->where($condition)->all();
}
} else {
// single primary key.
$models = $modelClass::findAll(array_values($ids));
}
}
return Yii::createObject([
'class' => BatchModels::className(),
'data' => $models
]);
}
/**
* 根据指定的多个主键,准备多个数据模型。
*
* 该方法依次执行以下步骤:
* 1. 调用 [[findModels()]],查找多个数据模型;
* 2. 循环调用 [[afterPrepareModel()]],触发 [[EVENT_AFTER_PREPARE_MODEL]] 事件;
*
* @param string $ids 模型IDs。
* @return BatchModels 查询到的模型列表。
*/
public function prepareModels($ids)
{
// 根据指定的多个主键,返回多个数据模型。
$models = $this->findModels($ids);
foreach ($models as $model) {
// 执行在准备完模型后的方法和事件。
$this->afterPrepareModel($model);
}
// 返回模型列表。
return $models;
}
/**
* 确认并返回有权限的模型列表。
*
* 该方法使用 [[$checkModelAccess]] 回调方法,检查并且过滤掉没有权限的模型。
*
* @param BatchModels $models 模型列表。
* @return BatchModels 有权限的模型列表。
*/
public function ensureModelsAccess($models)
{
if ($this->checkModelAccess) {
foreach ($models as $key => $model) {
try {
call_user_func($this->checkModelAccess, $model, $this);
} catch (ForbiddenHttpException $e) {
// 移除没有权限的模型。
unset($models[$key]);
continue;
}
}
}
return $models;
}
/**
* 在处理完模型列表后调用此方法。
* 默认实现了触发 [[EVENT_AFTER_PROCESS_MODELS]] 事件。
*
* @param object $object 对像实例。
*/
public function afterProcessModels($object)
{
$event = Yii::createObject([
'class' => ActionEvent::className(),
'object' => $object,
]);
$this->trigger(self::EVENT_AFTER_PROCESS_MODELS, $event);
}
}