Passed
Push — master ( fd9eda...12305d )
by Julien
05:21
created

Blameable   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 128
Duplicated Lines 0 %

Test Coverage

Coverage 11.59%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 22
eloc 74
c 1
b 0
f 0
dl 0
loc 128
ccs 8
cts 69
cp 0.1159
rs 10

4 Methods

Rating   Name   Duplication   Size   Complexity  
C createAudit() 0 64 12
A collectData() 0 11 2
A __construct() 0 6 1
B notify() 0 17 7
1
<?php
2
3
/**
4
 * This file is part of the Zemit Framework.
5
 *
6
 * (c) Zemit Team <[email protected]>
7
 *
8
 * For the full copyright and license information, please view the LICENSE.txt
9
 * file that was distributed with this source code.
10
 */
11
12
namespace Zemit\Mvc\Model\Behavior;
13
14
use Phalcon\Mvc\Model;
15
use Phalcon\Mvc\Model\Behavior;
16
use Phalcon\Mvc\ModelInterface;
17
use Zemit\Models\Audit;
18
use Zemit\Models\AuditDetail;
19
use Zemit\Models\Interfaces\AbstractInterface;
20
use Zemit\Models\Interfaces\AuditDetailInterface;
21
use Zemit\Models\Interfaces\AuditInterface;
22
use Zemit\Models\User;
23
use Zemit\Support\Helper;
24
25
/**
26
 * Zemit\Mvc\Model\Behavior\Blameable
27
 *
28
 * Allows to automatically update a model’s attribute saving the datetime when a
29
 * record is created or updated
30
 */
31
class Blameable extends Behavior
32
{
33
    use SkippableTrait;
34
    
35
    protected static ?int $parentId = null;
36
37
    protected ?array $snapshot = null;
38
39
    protected ?array $changedFields = null;
40
    
41
    protected string $auditClass = Audit::class;
42
43
    protected string $auditDetailClass = AuditDetail::class;
44
45
    protected string $userClass = User::class;
46
    
47 2
    public function __construct(array $options = [])
48
    {
49 2
        parent::__construct($options);
50 2
        $this->userClass = $options['userClass'] ?? $this->userClass;
51 2
        $this->auditClass = $options['auditClass'] ?? $this->auditClass;
52 2
        $this->auditDetailClass = $options['auditDetailClass'] ?? $this->auditDetailClass;
53
    }
54
    
55 2
    public function notify(string $type, ModelInterface $model)
56
    {
57 2
        if ($this->isEnabled()) {
58 2
            return null;
59
        }
60
        
61
        // prevent auditing audit & audit_detail tables
62
        if ($model instanceof $this->auditClass || $model instanceof $this->auditDetailClass) {
63
            return null;
64
        }
65
        
66
        switch ($type) {
67
            case 'afterCreate':
68
            case 'afterUpdate':
69
                return $this->createAudit($type, $model);
70
            case 'beforeUpdate':
71
                return $this->collectData($model);
72
        }
73
    }
74
    
75
    /**
76
     * Create new audit
77
     * Return true if the audit was created
78
     */
79
    public function createAudit(string $type, AbstractInterface $model): bool
80
    {
81
        $event = lcfirst(Helper::uncamelize(str_replace(['before', 'after'], ['', ''], $type)));
82
        
83
        $auditClass = $this->auditClass;
84
        $auditDetailClass = $this->auditDetailClass;
85
        
86
        $metaData = $model->getModelsMetaData();
87
        $columns = $metaData->getAttributes($model);
88
        $columnMap = $metaData->getColumnMap($model);
89
        $changedFields = $this->changedFields;
90
        $snapshot = $this->snapshot;
91
        
92
        $audit = new $auditClass();
93
        assert($audit instanceof AuditInterface);
94
        assert($audit instanceof Model);
95
        
96
        $audit->setModel(get_class($model));
97
        $audit->setTable($model->getSource());
98
        $audit->setPrimary($model->getId());
99
        $audit->setEvent($event);
100
        $audit->setColumns(json_encode($columnMap ?: $columns));
101
        $audit->setBefore($snapshot ? json_encode($snapshot) : null);
102
        $audit->setAfter(json_encode($model->toArray()));
0 ignored issues
show
Bug introduced by
The method toArray() does not exist on Zemit\Models\Interfaces\AbstractInterface. It seems like you code against a sub-type of Zemit\Models\Interfaces\AbstractInterface such as Zemit\Models\Setting or Zemit\Models\Category or Zemit\Models\Audit or Zemit\Models\UserGroup or Zemit\Models\User or Zemit\Models\Field or Zemit\Models\Page or Zemit\Models\Log or Zemit\Models\File or Zemit\Models\Role or Zemit\Models\GroupRole or Zemit\Models\Template or Zemit\Models\AuditDetail or Zemit\Models\UserType or Zemit\Models\PhalconMigrations or Zemit\Models\Post or Zemit\Models\PostCategory or Zemit\Models\Session or Zemit\Models\TranslateField or Zemit\Models\Table or Zemit\Models\GroupType or Zemit\Models\Translate or Zemit\Models\WorkspaceLang or Zemit\Models\Email or Zemit\Models\Workspace or Zemit\Models\Data or Zemit\Models\Group or Zemit\Models\Lang or Zemit\Models\EmailFile or Zemit\Models\TranslateTable or Zemit\Models\UserRole or Zemit\Models\Flag or Zemit\Models\Menu or Zemit\Models\Type or Zemit\Models\Meta. ( Ignorable by Annotation )

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

102
        $audit->setAfter(json_encode($model->/** @scrutinizer ignore-call */ toArray()));
Loading history...
103
        $audit->setParentId(self::$parentId);
104
        
105
        $auditDetailList = [];
106
        
107
        foreach ($columns as $column) {
108
            $map = empty($columnMap) ? $column : $columnMap[$column];
109
            $before = $snapshot[$map] ?? null;
110
            $after = $model->readAttribute($map);
0 ignored issues
show
Bug introduced by
The method readAttribute() does not exist on Zemit\Models\Interfaces\AbstractInterface. It seems like you code against a sub-type of Zemit\Models\Interfaces\AbstractInterface such as Zemit\Models\Setting or Zemit\Models\Category or Zemit\Models\Audit or Zemit\Models\UserGroup or Zemit\Models\User or Zemit\Models\Field or Zemit\Models\Page or Zemit\Models\Log or Zemit\Models\File or Zemit\Models\Role or Zemit\Models\GroupRole or Zemit\Models\Template or Zemit\Models\AuditDetail or Zemit\Models\UserType or Zemit\Models\PhalconMigrations or Zemit\Models\Post or Zemit\Models\PostCategory or Zemit\Models\Session or Zemit\Models\TranslateField or Zemit\Models\Table or Zemit\Models\GroupType or Zemit\Models\Translate or Zemit\Models\WorkspaceLang or Zemit\Models\Email or Zemit\Models\Workspace or Zemit\Models\Data or Zemit\Models\Group or Zemit\Models\Lang or Zemit\Models\EmailFile or Zemit\Models\TranslateTable or Zemit\Models\UserRole or Zemit\Models\Flag or Zemit\Models\Menu or Zemit\Models\Type or Zemit\Models\Meta. ( Ignorable by Annotation )

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

110
            /** @scrutinizer ignore-call */ 
111
            $after = $model->readAttribute($map);
Loading history...
111
            
112
            // skip unchanged
113
            if ($event === 'update' && $changedFields !== null && $snapshot !== null) {
114
                if ($before === $after || !in_array($map, $changedFields, true)) {
115
                    continue;
116
                }
117
            }
118
            
119
            $auditDetail = new $auditDetailClass();
120
            assert($auditDetail instanceof AuditDetailInterface);
121
            
122
            $auditDetail->setModel(get_class($model));
123
            $auditDetail->setTable($model->getSource());
124
            $auditDetail->setPrimary($model->getId());
125
            $auditDetail->setEvent($event);
126
            $auditDetail->setColumn($column);
127
            $auditDetail->setMap($map);
128
            $auditDetail->setBefore($before);
129
            $auditDetail->setAfter($after);
130
            
131
            $auditDetailList[] = $auditDetail;
132
        }
133
        
134
        $audit->AuditDetailList = $auditDetailList;
135
        $save = $audit->save();
136
        foreach ($audit->getMessages() as $message) {
137
            $message->setField('Audit.' . $message->getField());
138
            $model->appendMessage($message);
139
        }
140
        
141
        self::$parentId = (!empty($model->hasDirtyRelated())) ? $audit->getId() : null;
0 ignored issues
show
Bug introduced by
The method hasDirtyRelated() does not exist on Zemit\Models\Interfaces\AbstractInterface. It seems like you code against a sub-type of Zemit\Models\Interfaces\AbstractInterface such as Zemit\Models\Setting or Zemit\Models\Category or Zemit\Models\Audit or Zemit\Models\UserGroup or Zemit\Models\User or Zemit\Models\Field or Zemit\Models\Page or Zemit\Models\Log or Zemit\Models\File or Zemit\Models\Role or Zemit\Models\GroupRole or Zemit\Models\Template or Zemit\Models\AuditDetail or Zemit\Models\UserType or Zemit\Models\PhalconMigrations or Zemit\Models\Post or Zemit\Models\PostCategory or Zemit\Models\Session or Zemit\Models\TranslateField or Zemit\Models\Table or Zemit\Models\GroupType or Zemit\Models\Translate or Zemit\Models\WorkspaceLang or Zemit\Models\Email or Zemit\Models\Workspace or Zemit\Models\Data or Zemit\Models\Group or Zemit\Models\Lang or Zemit\Models\EmailFile or Zemit\Models\TranslateTable or Zemit\Models\UserRole or Zemit\Models\Flag or Zemit\Models\Menu or Zemit\Models\Type or Zemit\Models\Meta. ( Ignorable by Annotation )

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

141
        self::$parentId = (!empty($model->/** @scrutinizer ignore-call */ hasDirtyRelated())) ? $audit->getId() : null;
Loading history...
142
        return $save;
143
    }
144
    
145
    /**
146
     * Return true if data has been collected
147
     */
148
    protected function collectData(ModelInterface $model): bool
149
    {
150
        if ($model->hasSnapshotData()) {
0 ignored issues
show
Bug introduced by
The method hasSnapshotData() does not exist on Phalcon\Mvc\ModelInterface. It seems like you code against a sub-type of Phalcon\Mvc\ModelInterface such as Phalcon\Mvc\Model or Zemit\Models\Setting or Zemit\Models\Category or Zemit\Models\Audit or Zemit\Models\UserGroup or Zemit\Models\User or Zemit\Models\Field or Zemit\Models\Page or Zemit\Models\Log or Zemit\Models\File or Zemit\Models\Role or Zemit\Models\GroupRole or Zemit\Models\Template or Zemit\Models\AuditDetail or Zemit\Models\UserType or Zemit\Models\PhalconMigrations or Zemit\Models\Post or Zemit\Models\PostCategory or Zemit\Models\Session or Zemit\Models\TranslateField or Zemit\Models\Table or Zemit\Models\GroupType or Zemit\Models\Translate or Zemit\Models\WorkspaceLang or Zemit\Models\Email or Zemit\Models\Workspace or Zemit\Models\Data or Zemit\Models\Group or Zemit\Models\Lang or Zemit\Models\EmailFile or Zemit\Models\TranslateTable or Zemit\Models\UserRole or Zemit\Models\Flag or Zemit\Models\Menu or Zemit\Models\Type or Zemit\Models\Meta. ( Ignorable by Annotation )

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

150
        if ($model->/** @scrutinizer ignore-call */ hasSnapshotData()) {
Loading history...
151
            $this->snapshot = $model->getSnapshotData();
0 ignored issues
show
Bug introduced by
The method getSnapshotData() does not exist on Phalcon\Mvc\ModelInterface. It seems like you code against a sub-type of Phalcon\Mvc\ModelInterface such as Phalcon\Mvc\Model or Zemit\Models\Setting or Zemit\Models\Category or Zemit\Models\Audit or Zemit\Models\UserGroup or Zemit\Models\User or Zemit\Models\Field or Zemit\Models\Page or Zemit\Models\Log or Zemit\Models\File or Zemit\Models\Role or Zemit\Models\GroupRole or Zemit\Models\Template or Zemit\Models\AuditDetail or Zemit\Models\UserType or Zemit\Models\PhalconMigrations or Zemit\Models\Post or Zemit\Models\PostCategory or Zemit\Models\Session or Zemit\Models\TranslateField or Zemit\Models\Table or Zemit\Models\GroupType or Zemit\Models\Translate or Zemit\Models\WorkspaceLang or Zemit\Models\Email or Zemit\Models\Workspace or Zemit\Models\Data or Zemit\Models\Group or Zemit\Models\Lang or Zemit\Models\EmailFile or Zemit\Models\TranslateTable or Zemit\Models\UserRole or Zemit\Models\Flag or Zemit\Models\Menu or Zemit\Models\Type or Zemit\Models\Meta. ( Ignorable by Annotation )

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

151
            /** @scrutinizer ignore-call */ 
152
            $this->snapshot = $model->getSnapshotData();
Loading history...
152
            $this->changedFields = $model->getChangedFields();
0 ignored issues
show
Bug introduced by
The method getChangedFields() does not exist on Phalcon\Mvc\ModelInterface. It seems like you code against a sub-type of Phalcon\Mvc\ModelInterface such as Phalcon\Mvc\Model or Zemit\Models\Setting or Zemit\Models\Category or Zemit\Models\Audit or Zemit\Models\UserGroup or Zemit\Models\User or Zemit\Models\Field or Zemit\Models\Page or Zemit\Models\Log or Zemit\Models\File or Zemit\Models\Role or Zemit\Models\GroupRole or Zemit\Models\Template or Zemit\Models\AuditDetail or Zemit\Models\UserType or Zemit\Models\PhalconMigrations or Zemit\Models\Post or Zemit\Models\PostCategory or Zemit\Models\Session or Zemit\Models\TranslateField or Zemit\Models\Table or Zemit\Models\GroupType or Zemit\Models\Translate or Zemit\Models\WorkspaceLang or Zemit\Models\Email or Zemit\Models\Workspace or Zemit\Models\Data or Zemit\Models\Group or Zemit\Models\Lang or Zemit\Models\EmailFile or Zemit\Models\TranslateTable or Zemit\Models\UserRole or Zemit\Models\Flag or Zemit\Models\Menu or Zemit\Models\Type or Zemit\Models\Meta. ( Ignorable by Annotation )

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

152
            /** @scrutinizer ignore-call */ 
153
            $this->changedFields = $model->getChangedFields();
Loading history...
153
            return true;
154
        }
155
    
156
        $this->snapshot = null;
157
        $this->changedFields = null;
158
        return false;
159
    }
160
}
161