Completed
Push — master ( 2a9c44...03a236 )
by Brett
23s
created

AuditTrailBehavior::getNewOverrideValues()   A

↳ Parent: AuditTrailBehavior

Complexity

Conditions 1
Paths 1

Duplication

Lines 0
Ratio 0 %

Size

Total Lines 12
Code Lines 7

Code Coverage

Tests 10
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 7
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 12
ccs 10
cts 10
cp 1
crap 1
rs 9.4285
1
<?php
2
namespace bedezign\yii2\audit;
3
4
use Yii;
5
use yii\base\Exception;
6
use yii\db\ActiveRecord;
7
8
use bedezign\yii2\audit\models\AuditTrail;
9
use yii\web\Application;
10
11
use yii\db\Query;
12
13
/**
14
 * Class AuditTrailBehavior
15
 * @package bedezign\yii2\audit
16
 *
17
 * @property \yii\db\ActiveRecord $owner
18
 */
19
class AuditTrailBehavior extends \yii\base\Behavior
20
{
21
22
    /**
23
     * Array with fields to save
24
     * You don't need to configure both `allowed` and `ignored`
25
     * @var array
26
     */
27
    public $allowed = [];
28
29
    /**
30
     * Array with fields to ignore
31
     * You don't need to configure both `allowed` and `ignored`
32
     * @var array
33
     */
34
    public $ignored = [];
35
36
    /**
37
     * Array with classes to ignore
38
     * @var array
39
     */
40
    public $ignoredClasses = [];
41
42
    /**
43
     * Is the behavior is active or not
44
     * @var boolean
45
     */
46
    public $active = true;
47
48
    /**
49
     * Date format to use in stamp - set to "Y-m-d H:i:s" for datetime or "U" for timestamp
50
     * @var string
51
     */
52
    public $dateFormat = 'Y-m-d H:i:s';
53
54
    /**
55
     * @var array
56
     */
57
    private $_oldAttributes = [];
58
59
    /**
60 304
     * Array with fields you want to override before saving the row into audit_trail table
61
     * @var array
62
     */
63 304
    public $override = [];
64 304
65 304
66 304
    /**
67 114
     * @inheritdoc
68
     */
69
    public function events()
70
    {
71
        return [
72
            ActiveRecord::EVENT_AFTER_FIND => 'afterFind',
73 37
            ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert',
74
            ActiveRecord::EVENT_AFTER_UPDATE => 'afterUpdate',
75 15
            ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete',
76 37
        ];
77
    }
78
79
    /**
80
     *
81 24
     */
82
    public function afterFind()
83 8
    {
84 8
        $this->setOldAttributes($this->owner->getAttributes());
85 24
    }
86
87
    /**
88
     *
89
     */
90 23
    public function afterInsert()
91
    {
92 9
        $this->audit('CREATE');
93 9
        $this->setOldAttributes($this->owner->getAttributes());
94 23
    }
95
96
    /**
97
     *
98
     */
99 12
    public function afterUpdate()
100
    {
101 4
        $this->audit('UPDATE');
102 4
        $this->setOldAttributes($this->owner->getAttributes());
103 12
    }
104
105
    /**
106
     *
107
     */
108
    public function afterDelete()
109 53
    {
110
        $this->audit('DELETE');
111
        $this->setOldAttributes([]);
112 53
    }
113 9
114
    /**
115
     * @param $action
116 16
     * @throws \yii\db\Exception
117 9
     */
118
    public function audit($action)
119
    {
120 35
        // Not active? get out of here
121 2
        if (!$this->active) {
122 6
            return;
123
        }
124
        // Lets check if the whole class should be ignored
125 11
        if (sizeof($this->ignoredClasses) > 0 && array_search(get_class($this->owner), $this->ignoredClasses) !== false) {
126 29
            return;
127
        }
128
        // If this is a delete then just write one row and get out of here
129
        if ($action == 'DELETE') {
130
            $this->saveAuditTrailDelete();
131
            return;
132
        }
133
        // Now lets actually write the attributes
134 11
        $this->auditAttributes($action);
135
    }
136 11
137 11
    /**
138 11
     * Clean attributes of fields that are not allowed or ignored.
139
     *
140
     * @param $attributes
141
     * @return mixed
142
     */
143
    protected function cleanAttributes($attributes)
144
    {
145
        $attributes = $this->cleanAttributesAllowed($attributes);
146
        $attributes = $this->cleanAttributesIgnored($attributes);
147 11
        $attributes = $this->cleanAttributesOverride($attributes);
148
        return $attributes;
149 11
    }
150 2
151 2
    /**
152 2
     * Unset attributes which are not allowed
153 2
     *
154 2
     * @param $attributes
155 2
     * @return mixed
156 11
     */
157
    protected function cleanAttributesAllowed($attributes)
158
    {
159
        if (sizeof($this->allowed) > 0) {
160
            foreach ($attributes as $f => $v) {
161
                if (array_search($f, $this->allowed) === false) {
162
                    unset($attributes[$f]);
163
                }
164
            }
165 11
        }
166
        return $attributes;
167 11
    }
168 2
169 2
    /**
170 2
     * Unset attributes which are ignored
171 2
     *
172 2
     * @param $attributes
173 2
     * @return mixed
174 11
     */
175
    protected function cleanAttributesIgnored($attributes)
176
    {
177
        if (sizeof($this->ignored) > 0) {
178
            foreach ($attributes as $f => $v) {
179
                if (array_search($f, $this->ignored) !== false) {
180
                    unset($attributes[$f]);
181 27
                }
182
            }
183
        }
184 11
        return $attributes;
185 11
    }
186
187 11
    /**
188 3
     * attributes which need to get override with a new value
189
     *
190
     * @param $attributes
191 10
     * @return mixed
192 10
     */
193 10
    protected function cleanAttributesOverride($attributes)
194 10
    {
195 10
        if (sizeof($this->override) > 0 && sizeof($attributes) >0) {
196
            foreach ($this->override as $field => $queryParams) {
197 10
                $newOverrideValues = $this->getNewOverrideValues($attributes[$field], $queryParams);
198 26
199
                if (count($newOverrideValues) >1) {
200
                    $attributes[$field] = implode(', ',
201
                                        \yii\helpers\ArrayHelper::map($newOverrideValues, $queryParams['returnField'], $queryParams['returnField'])
202
                    );
203
                } elseif (count($newOverrideValues) == 1) {
204
                    $attributes[$field] = $newOverrideValues[0][$queryParams['returnField']];
205
                }
206
            }
207
        }
208
        return $attributes;
209
    }
210
211
    /**
212
     * @param string $searchFieldValue
213 26
     * @param string $queryParams
214
     * @return mixed
215
     */
216 26
    private function getNewOverrideValues($searchFieldValue, $queryParams)
217 10
    {
218 14
        $query = new Query;
219
220 14
        $query->select($queryParams['returnField'])
221 16
              ->from($queryParams['tableName'])
222 10
              ->where([$queryParams['searchField'] => $searchFieldValue]);
223 10
224
        $rows = $query->all();
225 26
226 26
        return $rows;
227 10
    }
228 10
229 10
230 26
    /**
231
     * @param string $action
232
     * @throws \yii\db\Exception
233
     */
234
    protected function auditAttributes($action)
235 6
    {
236
        // Get the new and old attributes
237 2
        $newAttributes = $this->cleanAttributes($this->owner->getAttributes());
238 2
        $oldAttributes = $this->cleanAttributes($this->getOldAttributes());
239 6
        // If no difference then get out of here
240 6
        if (count(array_diff_assoc($newAttributes, $oldAttributes)) <= 0) {
241 6
            return;
242 6
        }
243 6
        // Get the trail data
244 6
        $entry_id = $this->getAuditEntryId();
245 2
        $user_id = $this->getUserId();
246 6
        $model = $this->owner->className();
247
        $model_id = $this->getNormalizedPk();
248
        $created = date($this->dateFormat);
249
250
        $this->saveAuditTrail($action, $newAttributes, $oldAttributes, $entry_id, $user_id, $model, $model_id, $created);
251 29
    }
252
253 29
    /**
254
     * Save the audit trails for a create or update action
255
     *
256
     * @param $action
257
     * @param $newAttributes
258
     * @param $oldAttributes
259 27
     * @param $entry_id
260
     * @param $user_id
261 27
     * @param $model
262 27
     * @param $model_id
263
     * @param $created
264
     * @throws \yii\db\Exception
265
     */
266
    protected function saveAuditTrail($action, $newAttributes, $oldAttributes, $entry_id, $user_id, $model, $model_id, $created)
267 12
    {
268
        // Build a list of fields to log
269 12
        $rows = array();
270 12
        foreach ($newAttributes as $field => $new) {
271
            $old = isset($oldAttributes[$field]) ? $oldAttributes[$field] : '';
272
            // If they are not the same lets write an audit log
273
            if ($new != $old) {
274
                $rows[] = [$entry_id, $user_id, $old, $new, $action, $model, $model_id, $field, $created];
275
            }
276 12
        }
277
        // Record the field changes with a batch insert
278 12
        if (!empty($rows)) {
279
            $columns = ['entry_id', 'user_id', 'old_value', 'new_value', 'action', 'model', 'model_id', 'field', 'created'];
280
            $audit = Audit::getInstance();
281
            $audit->getDb()->createCommand()->batchInsert(AuditTrail::tableName(), $columns, $rows)->execute();
282
        }
283
    }
284
285 32
    /**
286
     * Save the audit trails for a delete action
287 12
     */
288 32
    protected function saveAuditTrailDelete()
289
    {
290
        $audit = Audit::getInstance();
291 32
        $audit->getDb()->createCommand()->insert(AuditTrail::tableName(), [
292
            'action' => 'DELETE',
293
            'entry_id' => $this->getAuditEntryId(),
294 12
            'user_id' => $this->getUserId(),
295
            'model' => $this->owner->className(),
296
            'model_id' => $this->getNormalizedPk(),
297
            'created' => date($this->dateFormat),
298
        ])->execute();
299
    }
300
301
    /**
302
     * @return array
303
     */
304
    public function getOldAttributes()
305
    {
306
        return $this->_oldAttributes;
307
    }
308
309
    /**
310
     * @param $value
311
     */
312
    public function setOldAttributes($value)
313
    {
314
        $this->_oldAttributes = $value;
315
    }
316
317
    /**
318
     * @return string
319
     */
320
    protected function getNormalizedPk()
321
    {
322
        $pk = $this->owner->getPrimaryKey();
323
        return is_array($pk) ? json_encode($pk) : $pk;
324
    }
325
326
    /**
327
     * @return int|null|string
328
     */
329
    protected function getUserId()
330
    {
331
        return (Yii::$app instanceof Application && Yii::$app->user) ? Yii::$app->user->id : null;
332
    }
333
334
    /**
335
     * @return models\AuditEntry|null|static
336
     * @throws \Exception
337
     */
338
    protected function getAuditEntryId()
339
    {
340
        $module = Audit::getInstance();
341
        if (!$module) {
342
            $module = \Yii::$app->getModule(Audit::findModuleIdentifier());
343
        }
344
        if (!$module) {
345
            throw new \Exception('Audit module cannot be loaded');
346
        }
347
        return Audit::getInstance()->getEntry(true)->id;
348
    }
349
350
}
351