Passed
Push — master ( 307c5a...43d84c )
by Tõnis
02:41
created

MyActiveTrait::getIdentityId()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace andmemasin\myabstract\traits;
4
5
use andmemasin\myabstract\Closing;
6
use yii;
7
use andmemasin\helpers\DateHelper;
8
use yii\db\ActiveQuery;
9
use yii\helpers\Inflector;
10
use yii\helpers\StringHelper;
11
use yii\db\Query;
12
13
/**
14
 * General code to be used in MyActiveRecord as well as User class
15
 * That can not extend MyActiveRecord
16
 *
17
 * @property string $timeCreated
18
 * @property string $timeUpdated
19
 * @property string $timeClosed
20
 * @property DateHelper $dateHelper
21
 *
22
 * @package andmemasin\myabstract
23
 * @author Tonis Ormisson <[email protected]>
24
 */
25
trait MyActiveTrait {
26
27
28
    /**
29
     *
30
     * @var bool $is_logicDelete by default all deletes are logical deletes
31
     */
32
    public $is_logicDelete = true;
33
34
35
    // for updater & time & closer id
36
    public $userCreatedCol = 'user_created';
37
    public $userUpdatedCol = 'user_updated';
38
    public $userClosedCol = 'user_closed';
39
    public $timeCreatedCol = 'time_created';
40
    public $timeUpdatedCol = 'time_updated';
41
    public $timeClosedCol = 'time_closed';
42
43
44
    abstract function beforeDelete();
45
    abstract function setAttributes();
46
47
48
49
    /**
50
     * {@inheritdoc}
51
     */
52
    public function save($runValidation = true, $attributeNames = null)
53
    {
54
        $userId = $this->userId();
55
        if ($this->isNewRecord) {
56
            $this->{$this->timeClosedCol} = $this->dateHelper->getEndOfTime();
57
            $this->{$this->userCreatedCol} = $userId;
58
            $this->{$this->timeCreatedCol} = $this->dateHelper->getDatetime6();
59
        }
60
61
        $this->{$this->userUpdatedCol} = $userId;
62
        $this->{$this->timeUpdatedCol} = $this->dateHelper->getDatetime6();
63
        return parent::save($runValidation, $attributeNames);
64
65
    }
66
67
    /**
68
     * @return int
69
     * @throws yii\base\InvalidConfigException
70
     */
71
    protected function getIdentityId()
72
    {
73
        $identity = Yii::$app->user->identity;
74
        if (is_null($identity)) {
75
            throw new yii\base\InvalidConfigException();
76
        }
77
        return (int) $identity->getId();
78
79
    }
80
81
    /**
82
     * Get an user id for the record manipulation
83
     * @return integer
84
     */
85
    private function userId()
86
    {
87
        if (Yii::$app instanceof yii\console\Application) {
88
            return 1;
89
        }
90
        if (!isset(Yii::$app->user) || empty(Yii::$app->user->identity)) {
91
            return 1;
92
        }
93
        return $this->getIdentityId();
94
95
    }
96
97
98
99
    /**
100
     * Return a label for the model eg for display lists, selections
101
     * this method must be overridden
102
     * @return string
103
     */
104
    public function label() {
105
        return "";
106
    }
107
108
    /**
109
     * Get Model name for views.
110
     * This method needs to be overridden
111
     * @return string Model display name
112
     */
113
    public static function modelName()
114
    {
115
        // FIXME this is not OK
116
        return Inflector::camel2words(StringHelper::basename(self::tableName()));
117
    }
118
119
    /**
120
     * Override delete function to make it logical delete
121
     * {@inheritdoc}
122
     */
123
    public function delete() {
124
        if ($this->is_logicDelete) {
125
            $this->beforeDelete();
126
            // don't put new data if deleting
127
            $this->setAttributes($this->oldAttributes);
0 ignored issues
show
Unused Code introduced by
The call to andmemasin\myabstract\tr...eTrait::setAttributes() has too many arguments starting with $this->oldAttributes. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

127
            $this->/** @scrutinizer ignore-call */ 
128
                   setAttributes($this->oldAttributes);

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.

Loading history...
128
129
            // delete logically
130
            if ($this->userUpdatedCol) {
131
                $this->{$this->userUpdatedCol} = $this->getIdentityId();;
132
            }
133
            if ($this->userClosedCol) {
134
                $this->{$this->userClosedCol} = $this->getIdentityId();;
135
            }
136
137
            if ($this->timeUpdatedCol) {
138
                $this->{$this->timeUpdatedCol} = $this->dateHelper->getDatetime6();
139
            }
140
141
            if ($this->timeClosedCol) {
142
                $this->{$this->timeClosedCol} = $this->dateHelper->getDatetime6();
143
            }
144
145
            // don't validate on deleting
146
            if ($this->save(false)) {
147
                self::updateClosingTime(static::tableName());
148
                $this->afterDelete();
0 ignored issues
show
Bug introduced by
It seems like afterDelete() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

148
                $this->/** @scrutinizer ignore-call */ 
149
                       afterDelete();
Loading history...
149
                return true;
150
            } else {
151
                throw new yii\base\UserException('Error deleting model');
152
            }
153
154
        } else {
155
            // otherwise regular delete
156
            parent::delete();
157
            return true;
158
        }
159
160
    }
161
162
163
    public static function bulkCopy($objects, $replaceParams) {
164
        /**
165
         * @var yii\db\ActiveRecord $model
166
         */
167
        $model = new static;
168
        if (!empty($objects)) {
169
            $rows = [];
170
            $cols = [];
171
            foreach ($objects as $object) {
172
                if (!empty($object->attributes)) {
173
                    $row = $object->attributes;
174
                    $cols = $model->attributes();
175
                    foreach ($replaceParams as $key =>$value) {
176
                        // remove primary keys (assuming auto-increment)
177
                        foreach ($model->primaryKey as $pk) {
178
                            unset($row[$pk]);
179
                        }
180
                        // remove pk fields from cols
181
                        $cols = array_diff($cols, $model->primaryKey);
182
                        $row[$key] = $value;
183
                    }
184
                    $rows[] = $row;
185
                } else {
186
                    throw new yii\base\InvalidArgumentException('Missing object attributes in ' . get_called_class() . ' ' . __FUNCTION__);
187
                }
188
189
            }
190
            \Yii::$app->db->createCommand()->batchInsert(parent::tableName(), $cols, $rows)->execute();
191
192
        }
193
    }
194
195
    /**
196
     * Bulk delete (logic) objects based on the conditions set  in $params
197
     * NB! this does NOT call before/after delete
198
     * @param array $params Array with the WHERE conditions as per QueryBuilder eg ['id'=>1] or.. ['>','id',3]
199
     */
200
    public static function bulkDelete($params) {
201
        $dateHelper = new DateHelper();
202
203
        /**
204
         * @var \yii\db\ActiveRecord
205
         */
206
        $model = new static;
207
        if (!empty($params)) {
208
209
            $baseParams = [
210
                $model->timeClosedCol=>$dateHelper->getDatetime6(),
211
                $model->userClosedCol => (new static)->getIdentityId(),
212
                $model->timeUpdatedCol=>$dateHelper->getDatetime6(),
213
                $model->userUpdatedCol =>(new static)->getIdentityId(),
214
            ];
215
216
            $conditions = [];
217
            $conditions[] = 'and';
218
            $conditions[] = ['>', static::tableName() . ".`" . $model->timeClosedCol . '`', $dateHelper->getDatetime6()];
219
            $conditions[] = $params;
220
            \Yii::$app->db->createCommand()->update(parent::tableName(), $baseParams, $conditions)->execute();
221
            self::updateClosingTime(static::tableName());
222
223
        } else {
224
            throw new yii\base\InvalidArgumentException('No conditions defined for ' . get_called_class() . ' ' . __FUNCTION__);
225
        }
226
227
228
229
    }
230
231
232
    /**
233
     * {@inheritdoc}
234
     */
235
    public function rules() {
236
        return [
237
            [[$this->userCreatedCol, $this->userUpdatedCol, $this->timeCreatedCol, $this->timeUpdatedCol, $this->timeClosedCol], 'required'],
238
            [[$this->userCreatedCol, $this->userUpdatedCol, $this->userClosedCol], 'integer'],
239
            [[$this->timeCreatedCol, $this->timeUpdatedCol, $this->timeClosedCol], 'safe'],
240
        ];
241
    }
242
    /**
243
     * {@inheritdoc}
244
     */
245
    public function attributeLabels() {
246
        return [
247
            $this->userCreatedCol => Yii::t('app', 'Created by'),
248
            $this->userUpdatedCol => Yii::t('app', 'Updated by'),
249
            $this->userClosedCol => Yii::t('app', 'Closed by'),
250
            $this->timeCreatedCol => Yii::t('app', 'Created at'),
251
            $this->timeUpdatedCol => Yii::t('app', 'Updated at'),
252
            $this->timeClosedCol => Yii::t('app', 'Closed at'),
253
        ];
254
    }
255
256
    /**
257
     * Only returns models that have not been closed
258
     * {@inheritdoc}
259
     * @return ActiveQuery the newly created [[ActiveQuery]] instance.
260
     */
261
    public static function find() {
262
        $child = new static;
263
        $query = parent::find()
264
            ->andFilterWhere($child->timeClosedCondition());
265
        return  $query;
266
    }
267
268
    public function timeClosedCondition()
269
    {
270
        $lastClosingTime = self::lastClosingTime(static::tableName());
271
        return ['>', static::tableName() . ".`" . $this->timeClosedCol . '`', $lastClosingTime];
272
    }
273
274
275
    public static function getCount($filter = null) {
276
        $query = self::find();
277
        if ($filter) {
278
            $query->andFilterWhere($filter);
279
        }
280
        return $query->count();
281
    }
282
283
    /**
284
     * a general query that adds the UserStrings filter on top of original query
285
     * @return Query
286
     */
287
    public static function query() {
288
        $child = new static;
289
        $dateHelper = new DateHelper();
290
        return (new Query())->andFilterWhere(['>', parent::tableName() . ".`" . $child->timeClosedCol . '`', $dateHelper->getDatetime6()]);
291
    }
292
293
    /**
294
     * Copy a model to a new model while replacing some params with new values
295
     * @param \yii\db\ActiveRecord $model
296
     * @param array $map map of old model attribute as keys and new values as values
297
     * @return bool|static
298
     * @throws yii\base\UserException
299
     */
300
    public static function copy($model, $map) {
301
        $newModel = new static;
302
        $newModel->attributes = $model->attributes;
0 ignored issues
show
Bug Best Practice introduced by
The property attributes does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
303
        foreach ($map as $key => $value) {
304
            $newModel->{$key} = $value;
305
        }
306
        if ($newModel->save()) {
307
            return $newModel;
308
        } else {
309
            throw new yii\base\UserException('Error copying model');
310
        }
311
    }
312
313
    /**
314
     * @param string $tableName
315
     * @return mixed|string
316
     */
317
    private static function lastClosingTime($tableName) {
318
        $dateHelper = new DateHelper();
319
320
        if (!self::hasClosing($tableName)) {
321
            self::createClosingRow($tableName);
322
        }
323
        /** @var Closing $closing */
324
        $closing = Closing::findOne($tableName);
325
        if ($closing) {
0 ignored issues
show
introduced by
$closing is of type andmemasin\myabstract\Closing, thus it always evaluated to true. If $closing can have other possible types, add them to src/traits/MyActiveTrait.php:323
Loading history...
326
            return $closing->last_closing_time;
327
        }
328
        return $dateHelper->getDatetime6();
329
    }
330
331
    /**
332
     * @param string $tableName
333
     * @return bool
334
     */
335
    private static function hasClosing($tableName){
336
            $closing = Closing::findOne($tableName);
337
            return !($closing == null);
338
    }
339
340
    /**
341
     * @param $tableName
342
     * @return Closing
343
     */
344
    private static function createClosingRow($tableName) {
345
346
        if (!self::hasClosing($tableName)) {
347
            $dateHelper = new DateHelper();
348
            $closing = new Closing([
349
                'table_name'=>$tableName,
350
                'last_closing_time' => $dateHelper->getDatetime6(),
351
            ]);
352
            $closing->save();
353
            return $closing;
354
        }
355
        return null;
356
    }
357
358
    private static function updateClosingTime($tableName) {
359
        if (!self::hasClosing($tableName)) {
360
            self::createClosingRow($tableName);
361
        }
362
        /** @var Closing $closing */
363
        $closing = Closing::findOne($tableName);
364
        $dateHelper = new DateHelper();
365
        $closing->last_closing_time = $dateHelper->getDatetime6();
366
        $closing->save();
367
    }
368
369
    /**
370
     * @return string
371
     */
372
    public function getTimeCreated()
373
    {
374
        return $this->{$this->timeCreatedCol};
375
    }
376
377
    /**
378
     * @return string
379
     */
380
    public function getTimeUpdated()
381
    {
382
        return $this->{$this->timeUpdatedCol};
383
    }
384
385
    /**
386
     * @return string
387
     */
388
    public function getTimeClosed()
389
    {
390
        return $this->{$this->timeClosedCol};
391
    }
392
393
    /**
394
     * @return DateHelper
395
     */
396
    public function getDateHelper()
397
    {
398
        return new DateHelper();
399
    }
400
401
402
}
403