Completed
Push — master ( e7beb0...e638ee )
by Tõnis
05:12
created

MyActiveTrait::userId()   B

Complexity

Conditions 5
Paths 4

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 8
nc 4
nop 0
dl 0
loc 14
rs 8.8571
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
     * @var bool $is_logicDelete by default all deletes are logical deletes
29
     */
30
    public $is_logicDelete = true;
31
32
33
    // for updater & time & closer id
34
    public $userCreatedCol = 'user_created';
35
    public $userUpdatedCol = 'user_updated';
36
    public $userClosedCol = 'user_closed';
37
    public $timeCreatedCol = 'time_created';
38
    public $timeUpdatedCol = 'time_updated';
39
    public $timeClosedCol = 'time_closed';
40
41
42
    abstract function beforeDelete();
43
    abstract function setAttributes();
44
45
    /**
46
     * {@inheritdoc}
47
     */
48
    public function save($runValidation = true, $attributeNames = null)
49
    {
50
        $userId = $this->userId();
51
        if ($this->isNewRecord) {
52
            $this->{$this->timeClosedCol} = $this->dateHelper->getEndOfTime();
53
            $this->{$this->userCreatedCol} = $userId;
54
            $this->{$this->timeCreatedCol} = $this->dateHelper->getDatetime6();
55
        }
56
57
        $this->{$this->userUpdatedCol} = $userId;
58
        $this->{$this->timeUpdatedCol} = $this->dateHelper->getDatetime6();
59
        return parent::save($runValidation, $attributeNames);
60
61
    }
62
63
    /**
64
     * Get an user id for the record manipulation
65
     * @return integer
66
     */
67
    private function userId()
68
    {
69
        if (Yii::$app instanceof yii\console\Application) {
70
            return 1;
71
        }
72
        if (!isset(Yii::$app->user) || empty(Yii::$app->user->identity)) {
73
            return 1;
74
        }
75
76
        $identity = Yii::$app->user->identity;
77
        if (is_null($identity)) {
78
            throw new yii\base\InvalidConfigException();
79
        }
80
        return (int) $identity->getId();
81
    }
82
83
84
    /**
85
     * Return a label for the model eg for display lists, selections
86
     * this method must be overridden
87
     * @return string
88
     */
89
    public function label() {
90
        return "";
91
    }
92
93
    /**
94
     * Get Model name for views.
95
     * This method needs to be overridden
96
     * @return string Model display name
97
     */
98
    public static function modelName()
99
    {
100
        // FIXME this is not OK
101
        return Inflector::camel2words(StringHelper::basename(self::tableName()));
102
    }
103
104
    /**
105
     * Override delete function to make it logical delete
106
     * {@inheritdoc}
107
     */
108
    public function delete() {
109
        if ($this->is_logicDelete) {
110
            $this->beforeDelete();
111
            // don't put new data if deleting
112
            $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

112
            $this->/** @scrutinizer ignore-call */ 
113
                   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...
113
114
            // delete logically
115
            if ($this->userUpdatedCol) {
116
                $this->{$this->userUpdatedCol} = Yii::$app->user->identity->getId();
0 ignored issues
show
Bug introduced by
The method getId() does not exist on null. ( Ignorable by Annotation )

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

116
                /** @scrutinizer ignore-call */ 
117
                $this->{$this->userUpdatedCol} = Yii::$app->user->identity->getId();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
117
            }
118
            if ($this->userClosedCol) {
119
                $this->{$this->userClosedCol} = Yii::$app->user->identity->getId();
120
            }
121
122
            if ($this->timeUpdatedCol) {
123
                $this->{$this->timeUpdatedCol} = $this->dateHelper->getDatetime6();
124
            }
125
126
            if ($this->timeClosedCol) {
127
                $this->{$this->timeClosedCol} = $this->dateHelper->getDatetime6();
128
            }
129
130
            // don't validate on deleting
131
            if ($this->save(false)) {
132
                self::updateClosingTime(static::tableName());
133
                $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

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