Total Complexity | 49 |
Total Lines | 391 |
Duplicated Lines | 0 % |
Changes | 15 | ||
Bugs | 6 | Features | 0 |
Complex classes like MyActiveTrait often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MyActiveTrait, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
25 | trait MyActiveTrait |
||
26 | { |
||
27 | |||
28 | |||
29 | /** |
||
30 | * |
||
31 | * @var bool $is_logicDelete by default all deletes are logical deletes |
||
32 | */ |
||
33 | public $is_logicDelete = true; |
||
34 | |||
35 | |||
36 | // for updater & time & closer id |
||
37 | public $userCreatedCol = 'user_created'; |
||
38 | public $userUpdatedCol = 'user_updated'; |
||
39 | public $userClosedCol = 'user_closed'; |
||
40 | public $timeCreatedCol = 'time_created'; |
||
41 | public $timeUpdatedCol = 'time_updated'; |
||
42 | public $timeClosedCol = 'time_closed'; |
||
43 | |||
44 | |||
45 | |||
46 | /** |
||
47 | * {@inheritdoc} |
||
48 | */ |
||
49 | public function save($runValidation = true, $attributeNames = null) |
||
61 | |||
62 | } |
||
63 | |||
64 | /** |
||
65 | * @return int |
||
66 | * @throws yii\base\InvalidConfigException |
||
67 | * @deprecated use userId() instead |
||
68 | */ |
||
69 | protected function getIdentityId() |
||
76 | |||
77 | } |
||
78 | |||
79 | /** |
||
80 | * Get an user id for the record manipulation |
||
81 | * @return integer |
||
82 | */ |
||
83 | private function userId() |
||
84 | { |
||
85 | |||
86 | if (Yii::$app instanceof yii\console\Application) { |
||
87 | return 1; |
||
88 | } |
||
89 | if (!isset(Yii::$app->user) || empty(Yii::$app->user->identity)) { |
||
90 | return 1; |
||
91 | } |
||
92 | $id = Yii::$app->user->id; |
||
93 | |||
94 | if (empty($id)) { |
||
95 | $id = 1; |
||
96 | } |
||
97 | return $id; |
||
|
|||
98 | |||
99 | } |
||
100 | |||
101 | |||
102 | |||
103 | /** |
||
104 | * Return a label for the model eg for display lists, selections |
||
105 | * this method must be overridden |
||
106 | * @return string |
||
107 | */ |
||
108 | public function label() { |
||
109 | return ""; |
||
110 | } |
||
111 | |||
112 | /** |
||
113 | * Get Model name for views. |
||
114 | * This method needs to be overridden |
||
115 | * @return string Model display name |
||
116 | */ |
||
117 | public static function modelName() |
||
118 | { |
||
119 | return Inflector::camel2words(StringHelper::basename(self::tableName())); |
||
120 | } |
||
121 | |||
122 | /** |
||
123 | * Override delete function to make it logical delete |
||
124 | * {@inheritdoc} |
||
125 | */ |
||
126 | public function delete() { |
||
127 | if ($this->is_logicDelete) { |
||
128 | return $this->logicalDelete(); |
||
129 | } |
||
130 | return parent::delete(); |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * @return bool |
||
135 | * @throws yii\base\InvalidConfigException |
||
136 | * @throws yii\base\UserException |
||
137 | */ |
||
138 | private function logicalDelete() { |
||
139 | $this->beforeDelete(); |
||
140 | // don't put new data if deleting |
||
141 | $this->setAttributes($this->oldAttributes); |
||
142 | |||
143 | // delete logically |
||
144 | if ($this->userUpdatedCol) { |
||
145 | $this->{$this->userUpdatedCol} = $this->userId(); |
||
146 | } |
||
147 | if ($this->userClosedCol) { |
||
148 | $this->{$this->userClosedCol} = $this->userId(); |
||
149 | } |
||
150 | |||
151 | if ($this->timeUpdatedCol) { |
||
152 | $this->{$this->timeUpdatedCol} = $this->dateHelper->getDatetime6(); |
||
153 | } |
||
154 | |||
155 | if ($this->timeClosedCol) { |
||
156 | $this->{$this->timeClosedCol} = $this->dateHelper->getDatetime6(); |
||
157 | } |
||
158 | |||
159 | // don't validate on deleting |
||
160 | if ($this->save(false)) { |
||
161 | $this->updateClosingTime(static::tableName()); |
||
162 | $this->afterDelete(); |
||
163 | return 1; |
||
164 | } |
||
165 | |||
166 | throw new yii\base\UserException('Error deleting model'); |
||
167 | } |
||
168 | |||
169 | |||
170 | public static function bulkCopy($objects, $replaceParams) { |
||
171 | /** |
||
172 | * @var yii\db\ActiveRecord $model |
||
173 | */ |
||
174 | $model = new static; |
||
175 | if (!empty($objects)) { |
||
176 | $rows = []; |
||
177 | $cols = []; |
||
178 | foreach ($objects as $object) { |
||
179 | if (!empty($object->attributes)) { |
||
180 | $row = $object->attributes; |
||
181 | $cols = $model->attributes(); |
||
182 | foreach ($replaceParams as $key =>$value) { |
||
183 | // remove primary keys (assuming auto-increment) |
||
184 | foreach ($model->primaryKey() as $pk) { |
||
185 | unset($row[$pk]); |
||
186 | } |
||
187 | // remove pk fields from cols |
||
188 | $cols = array_diff($cols, $model->primaryKey()); |
||
189 | $row[$key] = $value; |
||
190 | } |
||
191 | $rows[] = $row; |
||
192 | } else { |
||
193 | throw new yii\base\InvalidArgumentException('Missing object attributes in ' . get_called_class() . ' ' . __FUNCTION__); |
||
194 | } |
||
195 | |||
196 | } |
||
197 | \Yii::$app->db->createCommand()->batchInsert(parent::tableName(), $cols, $rows)->execute(); |
||
198 | |||
199 | } |
||
200 | } |
||
201 | |||
202 | /** |
||
203 | * Bulk delete (logic) objects based on the conditions set in $params |
||
204 | * NB! this does NOT call before/after delete |
||
205 | * @param array $params Array with the WHERE conditions as per QueryBuilder eg ['id'=>1] or.. ['>','id',3] |
||
206 | */ |
||
207 | public static function bulkDelete($params) { |
||
208 | $dateHelper = new DateHelper(); |
||
209 | |||
210 | /** |
||
211 | * @var \yii\db\ActiveRecord |
||
212 | */ |
||
213 | $model = new static; |
||
214 | if (!empty($params)) { |
||
215 | |||
216 | $baseParams = [ |
||
217 | $model->timeClosedCol=>$dateHelper->getDatetime6(), |
||
218 | $model->userClosedCol => $model->userId(), |
||
219 | $model->timeUpdatedCol=>$dateHelper->getDatetime6(), |
||
220 | $model->userUpdatedCol =>$model->userId(), |
||
221 | ]; |
||
222 | |||
223 | $conditions = []; |
||
224 | $conditions[] = 'and'; |
||
225 | $conditions[] = ['>', static::tableName() . ".`" . $model->timeClosedCol . '`', $dateHelper->getDatetime6()]; |
||
226 | $conditions[] = $params; |
||
227 | \Yii::$app->db->createCommand()->update(parent::tableName(), $baseParams, $conditions)->execute(); |
||
228 | $model->updateClosingTime(static::tableName()); |
||
229 | |||
230 | } else { |
||
231 | throw new yii\base\InvalidArgumentException('No conditions defined for ' . get_called_class() . ' ' . __FUNCTION__); |
||
232 | } |
||
233 | |||
234 | |||
235 | |||
236 | } |
||
237 | |||
238 | |||
239 | /** |
||
240 | * {@inheritdoc} |
||
241 | */ |
||
242 | public function rules() { |
||
243 | return [ |
||
244 | [[$this->userCreatedCol, $this->userUpdatedCol, $this->userClosedCol], 'integer'], |
||
245 | [[$this->timeCreatedCol, $this->timeUpdatedCol, $this->timeClosedCol], 'safe'], |
||
246 | ]; |
||
247 | } |
||
248 | /** |
||
249 | * {@inheritdoc} |
||
250 | */ |
||
251 | public function attributeLabels() { |
||
252 | return [ |
||
253 | $this->userCreatedCol => Yii::t('app', 'Created by'), |
||
254 | $this->userUpdatedCol => Yii::t('app', 'Updated by'), |
||
255 | $this->userClosedCol => Yii::t('app', 'Closed by'), |
||
256 | $this->timeCreatedCol => Yii::t('app', 'Created at'), |
||
257 | $this->timeUpdatedCol => Yii::t('app', 'Updated at'), |
||
258 | $this->timeClosedCol => Yii::t('app', 'Closed at'), |
||
259 | ]; |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Only returns models that have not been closed |
||
264 | * {@inheritdoc} |
||
265 | * @return ActiveQuery the newly created [[ActiveQuery]] instance. |
||
266 | */ |
||
267 | public static function find() { |
||
268 | $child = new static; |
||
269 | $query = parent::find() |
||
270 | ->andFilterWhere($child->timeClosedCondition()); |
||
271 | return $query; |
||
272 | } |
||
273 | |||
274 | public function timeClosedCondition() |
||
275 | { |
||
276 | $lastClosingTime = $this->lastClosingTime(static::tableName()); |
||
277 | return ['>', static::tableName() . ".`" . $this->timeClosedCol . '`', $lastClosingTime]; |
||
278 | } |
||
279 | |||
280 | |||
281 | public static function getCount($filter = null) { |
||
282 | $query = self::find(); |
||
283 | if ($filter) { |
||
284 | $query->andFilterWhere($filter); |
||
285 | } |
||
286 | return $query->count(); |
||
287 | } |
||
288 | |||
289 | /** |
||
290 | * a general query that adds the UserStrings filter on top of original query |
||
291 | * @return Query |
||
292 | */ |
||
293 | public static function query() { |
||
294 | $child = new static; |
||
295 | $dateHelper = new DateHelper(); |
||
296 | return (new Query())->andFilterWhere(['>', parent::tableName() . ".`" . $child->timeClosedCol . '`', $dateHelper->getDatetime6()]); |
||
297 | } |
||
298 | |||
299 | /** |
||
300 | * Copy a model to a new model while replacing some params with new values |
||
301 | * @param \yii\db\ActiveRecord $model |
||
302 | * @param array $map map of old model attribute as keys and new values as values |
||
303 | * @return bool|static |
||
304 | * @throws yii\base\UserException |
||
305 | */ |
||
306 | public static function copy($model, $map) { |
||
307 | $newModel = new static; |
||
308 | $newModel->attributes = $model->attributes; |
||
309 | foreach ($map as $key => $value) { |
||
310 | $newModel->{$key} = $value; |
||
311 | } |
||
312 | if ($newModel->save()) { |
||
313 | return $newModel; |
||
314 | } |
||
315 | throw new yii\base\UserException('Error copying model'); |
||
316 | } |
||
317 | |||
318 | /** |
||
319 | * @param string $tableName |
||
320 | * @return mixed|string |
||
321 | */ |
||
322 | private function lastClosingTime($tableName) { |
||
323 | $cacheKey = "closing:time:{$tableName}"; |
||
324 | return Yii::$app->cache->getOrSet($cacheKey, function () use ($tableName) { |
||
325 | $dateHelper = new DateHelper(); |
||
326 | |||
327 | if (!$this->hasClosing($tableName)) { |
||
328 | $this->createClosingRow($tableName); |
||
329 | } |
||
330 | /** @var Closing $closing */ |
||
331 | $closing = Closing::findOne($tableName); |
||
332 | if ($closing) { |
||
333 | return $closing->last_closing_time; |
||
334 | } |
||
335 | return $dateHelper->getDatetime6(); |
||
336 | }); |
||
337 | } |
||
338 | |||
339 | /** |
||
340 | * @param string $tableName |
||
341 | * @return bool |
||
342 | */ |
||
343 | private function hasClosing($tableName) |
||
344 | { |
||
345 | $cacheKey = "closing:has:{$tableName}"; |
||
346 | return Yii::$app->cache->getOrSet($cacheKey, function () use ($tableName) { |
||
347 | $closing = Closing::findOne($tableName); |
||
348 | return !($closing == null); |
||
349 | }); |
||
350 | } |
||
351 | |||
352 | /** |
||
353 | * @param $tableName |
||
354 | * @return Closing |
||
355 | */ |
||
356 | private function createClosingRow($tableName) { |
||
357 | |||
358 | if (!$this->hasClosing($tableName)) { |
||
359 | $dateHelper = new DateHelper(); |
||
360 | $closing = new Closing([ |
||
361 | 'table_name'=>$tableName, |
||
362 | 'last_closing_time' => $dateHelper->getDatetime6(), |
||
363 | ]); |
||
364 | $closing->save(); |
||
365 | return $closing; |
||
366 | } |
||
367 | return null; |
||
368 | } |
||
369 | |||
370 | private function updateClosingTime($tableName) { |
||
383 | |||
384 | } |
||
385 | |||
386 | /** |
||
387 | * @return string |
||
388 | */ |
||
389 | public function getTimeCreated() |
||
390 | { |
||
391 | return $this->{$this->timeCreatedCol}; |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * @return string |
||
396 | */ |
||
397 | public function getTimeUpdated() |
||
398 | { |
||
399 | return $this->{$this->timeUpdatedCol}; |
||
400 | } |
||
401 | |||
402 | /** |
||
403 | * @return string |
||
404 | */ |
||
405 | public function getTimeClosed() |
||
406 | { |
||
407 | return $this->{$this->timeClosedCol}; |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * @return DateHelper |
||
412 | */ |
||
413 | public function getDateHelper() |
||
416 | } |
||
417 | |||
418 | |||
419 | } |
||
420 |