GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Completed
Pull Request — master (#191)
by Herbert
13:06
created

AuditTrailBehavior   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 343
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 7

Test Coverage

Coverage 97.22%

Importance

Changes 5
Bugs 0 Features 2
Metric Value
dl 0
loc 343
ccs 105
cts 108
cp 0.9722
rs 8.3157
c 5
b 0
f 2
wmc 43
lcom 1
cbo 7

19 Methods

Rating   Name   Duplication   Size   Complexity  
B saveAuditTrail() 0 18 5
A saveAuditTrailDelete() 0 12 1
A getOldAttributes() 0 4 1
A setOldAttributes() 0 4 1
A getNormalizedPk() 0 5 2
A getUserId() 0 4 1
A getAuditEntryId() 0 11 3
A events() 0 9 1
A afterFind() 0 4 1
A afterInsert() 0 5 1
A afterUpdate() 0 5 1
A afterDelete() 0 5 1
B audit() 0 18 5
A cleanAttributes() 0 7 1
A cleanAttributesAllowed() 0 11 4
B cleanAttributesIgnored() 0 15 5
B cleanAttributesOverride() 0 18 6
A getNewOverrideValues() 0 12 1
A auditAttributes() 0 18 2

How to fix   Complexity   

Complex Class

Complex classes like AuditTrailBehavior 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 AuditTrailBehavior, and based on these observations, apply Extract Interface, too.

1
<?php
2
namespace bedezign\yii2\audit;
3
4
use Yii;
5
use yii\base\Behavior;
6
use yii\db\ActiveRecord;
7
use bedezign\yii2\audit\models\AuditTrail;
8
use yii\db\Query;
9
10
/**
11
 * Class AuditTrailBehavior
12
 * @package bedezign\yii2\audit
13
 *
14
 * @property \yii\db\ActiveRecord $owner
15
 */
16
class AuditTrailBehavior extends Behavior
17
{
18
19
    /**
20
     * Array with fields to save
21
     * You don't need to configure both `allowed` and `ignored`
22
     * @var array
23
     */
24
    public $allowed = [];
25
26
    /**
27
     * Array with fields to ignore
28
     * You don't need to configure both `allowed` and `ignored`
29
     * @var array
30
     */
31
    public $ignored = [];
32
33
    /**
34
     * Array with classes to ignore
35
     * @var array
36
     */
37
    public $ignoredClasses = [];
38
39
    /**
40
     * Timestamp attributes should, in most cases, be ignored.
41
     * In case you want to log them, you can set this option to false.
42
     * @var array
43
     */
44
    public $ignore_timestamps = true;
45
46
    /**
47
     * Is the behavior is active or not
48
     * @var boolean
49
     */
50
    public $active = true;
51
52
    /**
53
     * Date format to use in stamp - set to "Y-m-d H:i:s" for datetime or "U" for timestamp
54
     * @var string
55
     */
56
    public $dateFormat = 'Y-m-d H:i:s';
57
58
    /**
59
     * @var array
60 304
     */
61
    private $_oldAttributes = [];
62
63 304
    /**
64 304
     * Array with fields you want to override before saving the row into audit_trail table
65 304
     * @var array
66 304
     */
67 114
    public $override = [];
68
69
    /**
70
     * @inheritdoc
71
     */
72
    public function events()
73 37
    {
74
        return [
75 15
            ActiveRecord::EVENT_AFTER_FIND => 'afterFind',
76 37
            ActiveRecord::EVENT_AFTER_INSERT => 'afterInsert',
77
            ActiveRecord::EVENT_AFTER_UPDATE => 'afterUpdate',
78
            ActiveRecord::EVENT_AFTER_DELETE => 'afterDelete',
79
        ];
80
    }
81 24
82
    /**
83 8
     *
84 8
     */
85 24
    public function afterFind()
86
    {
87
        $this->setOldAttributes($this->owner->getAttributes());
88
    }
89
90 23
    /**
91
     *
92 9
     */
93 9
    public function afterInsert()
94 23
    {
95
        $this->audit('CREATE');
96
        $this->setOldAttributes($this->owner->getAttributes());
97
    }
98
99 12
    /**
100
     *
101 4
     */
102 4
    public function afterUpdate()
103 12
    {
104
        $this->audit('UPDATE');
105
        $this->setOldAttributes($this->owner->getAttributes());
106
    }
107
108
    /**
109 53
     *
110
     */
111
    public function afterDelete()
112 53
    {
113 9
        $this->audit('DELETE');
114
        $this->setOldAttributes([]);
115
    }
116 16
117 9
    /**
118
     * @param $action
119
     * @throws \yii\db\Exception
120 35
     */
121 2
    public function audit($action)
122 6
    {
123
        // Not active? get out of here
124
        if (!$this->active) {
125 11
            return;
126 29
        }
127
        // Lets check if the whole class should be ignored
128
        if (sizeof($this->ignoredClasses) > 0 && array_search(get_class($this->owner), $this->ignoredClasses) !== false) {
129
            return;
130
        }
131
        // If this is a delete then just write one row and get out of here
132
        if ($action == 'DELETE') {
133
            $this->saveAuditTrailDelete();
134 11
            return;
135
        }
136 11
        // Now lets actually write the attributes
137 11
        $this->auditAttributes($action);
138 11
    }
139
140
    /**
141
     * Clean attributes of fields that are not allowed or ignored.
142
     *
143
     * @param $attributes
144
     * @return mixed
145
     */
146
    protected function cleanAttributes($attributes)
147 11
    {
148
        $attributes = $this->cleanAttributesAllowed($attributes);
149 11
        $attributes = $this->cleanAttributesIgnored($attributes);
150 2
        $attributes = $this->cleanAttributesOverride($attributes);
151 2
        return $attributes;
152 2
    }
153 2
154 2
    /**
155 2
     * Unset attributes which are not allowed
156 11
     *
157
     * @param $attributes
158
     * @return mixed
159
     */
160
    protected function cleanAttributesAllowed($attributes)
161
    {
162
        if (sizeof($this->allowed) > 0) {
163
            foreach ($attributes as $f => $v) {
164
                if (array_search($f, $this->allowed) === false) {
165 11
                    unset($attributes[$f]);
166
                }
167 11
            }
168 2
        }
169 2
        return $attributes;
170 2
    }
171 2
172 2
    /**
173 2
     * Unset attributes which are ignored
174 11
     *
175
     * @param $attributes
176
     * @return mixed
177
     */
178
    protected function cleanAttributesIgnored($attributes)
179
    {
180
        if($this->ignore_timestamps)
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->ignore_timestamps of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
181 27
            $this->ignored = array_merge($this->ignored, [
182
                'created', 'updated', 'created_at', 'updated_at', 'timestamp']);
183
184 11
        if (sizeof($this->ignored) > 0) {
185 11
            foreach ($attributes as $f => $v) {
186
                if (array_search($f, $this->ignored) !== false) {
187 11
                    unset($attributes[$f]);
188 3
                }
189
            }
190
        }
191 10
        return $attributes;
192 10
    }
193 10
194 10
    /**
195 10
     * attributes which need to get override with a new value
196
     *
197 10
     * @param $attributes
198 26
     * @return mixed
199
     */
200
    protected function cleanAttributesOverride($attributes)
201
    {
202
        if (sizeof($this->override) > 0 && sizeof($attributes) >0) {
203
            foreach ($this->override as $field => $queryParams) {
204
                $newOverrideValues = $this->getNewOverrideValues($attributes[$field], $queryParams);
205
                $saveField = \yii\helpers\ArrayHelper::getValue($queryParams, 'saveField', $field);
206
207
                if (count($newOverrideValues) >1) {
208
                    $attributes[$saveField] = implode(', ',
209
                                        \yii\helpers\ArrayHelper::map($newOverrideValues, $queryParams['returnField'], $queryParams['returnField'])
210
                    );
211
                } elseif (count($newOverrideValues) == 1) {
212
                    $attributes[$saveField] = $newOverrideValues[0][$queryParams['returnField']];
213 26
                }
214
            }
215
        }
216 26
        return $attributes;
217 10
    }
218 14
219
    /**
220 14
     * @param string $searchFieldValue
221 16
     * @param string $queryParams
222 10
     * @return mixed
223 10
     */
224
    private function getNewOverrideValues($searchFieldValue, $queryParams)
225 26
    {
226 26
        $query = new Query;
227 10
228 10
        $query->select($queryParams['returnField'])
229 10
              ->from($queryParams['tableName'])
230 26
              ->where([$queryParams['searchField'] => $searchFieldValue]);
231
232
        $rows = $query->all();
233
234
        return $rows;
235 6
    }
236
237 2
238 2
    /**
239 6
     * @param string $action
240 6
     * @throws \yii\db\Exception
241 6
     */
242 6
    protected function auditAttributes($action)
243 6
    {
244 6
        // Get the new and old attributes
245 2
        $newAttributes = $this->cleanAttributes($this->owner->getAttributes());
246 6
        $oldAttributes = $this->cleanAttributes($this->getOldAttributes());
247
        // If no difference then get out of here
248
        if (count(array_diff_assoc($newAttributes, $oldAttributes)) <= 0) {
249
            return;
250
        }
251 29
        // Get the trail data
252
        $entry_id = $this->getAuditEntryId();
253 29
        $user_id = $this->getUserId();
254
        $model = $this->owner->className();
255
        $model_id = $this->getNormalizedPk();
256
        $created = date($this->dateFormat);
257
258
        $this->saveAuditTrail($action, $newAttributes, $oldAttributes, $entry_id, $user_id, $model, $model_id, $created);
259 27
    }
260
261 27
    /**
262 27
     * Save the audit trails for a create or update action
263
     *
264
     * @param $action
265
     * @param $newAttributes
266
     * @param $oldAttributes
267 12
     * @param $entry_id
268
     * @param $user_id
269 12
     * @param $model
270 12
     * @param $model_id
271
     * @param $created
272
     * @throws \yii\db\Exception
273
     */
274
    protected function saveAuditTrail($action, $newAttributes, $oldAttributes, $entry_id, $user_id, $model, $model_id, $created)
275
    {
276 12
        // Build a list of fields to log
277
        $rows = array();
278 12
        foreach ($newAttributes as $field => $new) {
279
            $old = isset($oldAttributes[$field]) ? $oldAttributes[$field] : '';
280
            // If they are not the same lets write an audit log
281
            if ($new != $old) {
282
                $rows[] = [$entry_id, $user_id, $old, $new, $action, $model, $model_id, $field, $created];
283
            }
284
        }
285 32
        // Record the field changes with a batch insert
286
        if (!empty($rows)) {
287 12
            $columns = ['entry_id', 'user_id', 'old_value', 'new_value', 'action', 'model', 'model_id', 'field', 'created'];
288 32
            $audit = Audit::getInstance();
289
            $audit->getDb()->createCommand()->batchInsert(AuditTrail::tableName(), $columns, $rows)->execute();
290
        }
291 32
    }
292
293
    /**
294 12
     * Save the audit trails for a delete action
295
     */
296
    protected function saveAuditTrailDelete()
297
    {
298
        $audit = Audit::getInstance();
299
        $audit->getDb()->createCommand()->insert(AuditTrail::tableName(), [
300
            'action' => 'DELETE',
301
            'entry_id' => $this->getAuditEntryId(),
302
            'user_id' => $this->getUserId(),
303
            'model' => $this->owner->className(),
304
            'model_id' => $this->getNormalizedPk(),
305
            'created' => date($this->dateFormat),
306
        ])->execute();
307
    }
308
309
    /**
310
     * @return array
311
     */
312
    public function getOldAttributes()
313
    {
314
        return $this->_oldAttributes;
315
    }
316
317
    /**
318
     * @param $value
319
     */
320
    public function setOldAttributes($value)
321
    {
322
        $this->_oldAttributes = $value;
323
    }
324
325
    /**
326
     * @return string
327
     */
328
    protected function getNormalizedPk()
329
    {
330
        $pk = $this->owner->getPrimaryKey();
331
        return is_array($pk) ? json_encode($pk) : $pk;
332
    }
333
334
    /**
335
     * @return int|null|string
336
     */
337
    protected function getUserId()
338
    {
339
        return Audit::getInstance()->getUserId();
340
    }
341
342
    /**
343
     * @return models\AuditEntry|null|static
344
     * @throws \Exception
345
     */
346
    protected function getAuditEntryId()
347
    {
348
        $module = Audit::getInstance();
349
        if (!$module) {
350
            $module = \Yii::$app->getModule(Audit::findModuleIdentifier());
351
        }
352
        if (!$module) {
353
            throw new \Exception('Audit module cannot be loaded');
354
        }
355
        return Audit::getInstance()->getEntry(true)->id;
356
    }
357
358
}
359