Completed
Pull Request — master (#437)
by Quetzy
10:38
created

Audit   A

Complexity

Total Complexity 34

Size/Duplication

Total Lines 238
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 77
dl 0
loc 238
ccs 84
cts 84
cp 1
rs 9.68
c 0
b 0
f 0
wmc 34

11 Methods

Rating   Name   Duplication   Size   Complexity  
A auditable() 0 3 1
A getTable() 0 3 1
A getConnection() 0 3 1
A decodeAttributeValue() 0 15 3
A getMetadata() 0 17 5
A getTags() 0 3 1
A resolveData() 0 38 5
A getFormattedValue() 0 18 5
A getDataValue() 0 25 6
A user() 0 3 1
A getModified() 0 20 5
1
<?php
2
/**
3
 * This file is part of the Laravel Auditing package.
4
 *
5
 * @author     Antério Vieira <[email protected]>
6
 * @author     Quetzy Garcia  <[email protected]>
7
 * @author     Raphael França <[email protected]>
8
 * @copyright  2015-2018
9
 *
10
 * For the full copyright and license information,
11
 * please view the LICENSE.md file that was distributed
12
 * with this source code.
13
 */
14
15
namespace OwenIt\Auditing;
16
17
use DateTimeInterface;
18
use Illuminate\Database\Eloquent\Model;
19
use Illuminate\Database\Eloquent\Relations\MorphTo;
20
use Illuminate\Support\Facades\Config;
21
use OwenIt\Auditing\Contracts\AttributeEncoder;
22
23
trait Audit
24
{
25
    /**
26
     * Audit data.
27
     *
28
     * @var array
29
     */
30
    protected $data = [];
31
32
    /**
33
     * The Audit attributes that belong to the metadata.
34
     *
35
     * @var array
36
     */
37
    protected $metadata = [];
38
39
    /**
40
     * The Auditable attributes that were modified.
41
     *
42
     * @var array
43
     */
44
    protected $modified = [];
45
46
    /**
47
     * {@inheritdoc}
48
     */
49 138
    public function getConnection()
50
    {
51 138
        return static::resolveConnection(Config::get('audit.drivers.database.connection'));
52
    }
53
54
    /**
55
     * {@inheritdoc}
56
     */
57 138
    public function getTable(): string
58
    {
59 138
        return Config::get('audit.drivers.database.table', parent::getTable());
60
    }
61
62
    /**
63
     * {@inheritdoc}
64
     */
65 36
    public function auditable(): MorphTo
66
    {
67 36
        return $this->morphTo();
68
    }
69
70
    /**
71
     * {@inheritdoc}
72
     */
73 45
    public function user(): MorphTo
74
    {
75 45
        return $this->morphTo();
76
    }
77
78
    /**
79
     * {@inheritdoc}
80
     */
81 45
    public function resolveData(): array
82
    {
83 45
        $morphPrefix = Config::get('audit.user.morph_prefix', 'user');
84
85
        // Metadata
86 45
        $this->data = [
87 45
            'audit_id'         => $this->id,
88 45
            'audit_event'      => $this->event,
89 45
            'audit_url'        => $this->url,
90 45
            'audit_ip_address' => $this->ip_address,
91 45
            'audit_user_agent' => $this->user_agent,
92 45
            'audit_tags'       => $this->tags,
93 45
            'audit_created_at' => $this->serializeDate($this->created_at),
0 ignored issues
show
Bug introduced by
It seems like serializeDate() 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

93
            'audit_created_at' => $this->/** @scrutinizer ignore-call */ serializeDate($this->created_at),
Loading history...
94 45
            'audit_updated_at' => $this->serializeDate($this->updated_at),
95 45
            'user_id'          => $this->getAttribute($morphPrefix.'_id'),
0 ignored issues
show
Bug introduced by
It seems like getAttribute() 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

95
            'user_id'          => $this->/** @scrutinizer ignore-call */ getAttribute($morphPrefix.'_id'),
Loading history...
96 45
            'user_type'        => $this->getAttribute($morphPrefix.'_type'),
97
        ];
98
99 45
        if ($this->user) {
100 30
            foreach ($this->user->getArrayableAttributes() as $attribute => $value) {
101 30
                $this->data['user_'.$attribute] = $value;
102
            }
103
        }
104
105 45
        $this->metadata = array_keys($this->data);
106
107
        // Modified Auditable attributes
108 45
        foreach ($this->new_values as $key => $value) {
109 39
            $this->data['new_'.$key] = $value;
110
        }
111
112 45
        foreach ($this->old_values as $key => $value) {
113 9
            $this->data['old_'.$key] = $value;
114
        }
115
116 45
        $this->modified = array_diff_key(array_keys($this->data), $this->metadata);
117
118 45
        return $this->data;
119
    }
120
121
    /**
122
     * Get the formatted value of an Eloquent model.
123
     *
124
     * @param Model  $model
125
     * @param string $key
126
     * @param mixed  $value
127
     *
128
     * @return mixed
129
     */
130 30
    protected function getFormattedValue(Model $model, string $key, $value)
131
    {
132
        // Apply defined get mutator
133 30
        if ($model->hasGetMutator($key)) {
134 27
            return $model->mutateAttribute($key, $value);
135
        }
136
137
        // Cast to native PHP type
138 30
        if ($model->hasCast($key)) {
139 18
            return $model->castAttribute($key, $value);
140
        }
141
142
        // Honour DateTime attribute
143 30
        if ($value !== null && in_array($key, $model->getDates(), true)) {
144 15
            return $model->asDateTime($value);
145
        }
146
147 30
        return $value;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 36
    public function getDataValue(string $key)
154
    {
155 36
        if (!array_key_exists($key, $this->data)) {
156 3
            return;
157
        }
158
159 36
        $value = $this->data[$key];
160
161
        // User value
162 36
        if ($this->user && starts_with($key, 'user_')) {
163 9
            return $this->getFormattedValue($this->user, substr($key, 5), $value);
164
        }
165
166
        // Auditable value
167 36
        if ($this->auditable && starts_with($key, ['new_', 'old_'])) {
168 24
            $attribute = substr($key, 4);
169
170 24
            return $this->getFormattedValue(
171 24
                $this->auditable,
172 24
                $attribute,
173 24
                $this->decodeAttributeValue($this->auditable, $attribute, $value)
174
            );
175
        }
176
177 12
        return $value;
178
    }
179
180
    /**
181
     * Decode attribute value.
182
     *
183
     * @param Contracts\Auditable $auditable
184
     * @param string              $attribute
185
     * @param mixed               $value
186
     *
187
     * @return mixed
188
     */
189 24
    protected function decodeAttributeValue(Contracts\Auditable $auditable, string $attribute, $value)
190
    {
191 24
        $attributeModifiers = $auditable->getAttributeModifiers();
192
193 24
        if (!array_key_exists($attribute, $attributeModifiers)) {
194 24
            return $value;
195
        }
196
197 3
        $attributeDecoder = $attributeModifiers[$attribute];
198
199 3
        if (is_subclass_of($attributeDecoder, AttributeEncoder::class)) {
200 3
            return call_user_func([$attributeDecoder, 'decode'], $value);
201
        }
202
203 3
        return $value;
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209 12
    public function getMetadata(bool $json = false, int $options = 0, int $depth = 512)
210
    {
211 12
        if (empty($this->data)) {
212 12
            $this->resolveData();
213
        }
214
215 12
        $metadata = [];
216
217 12
        foreach ($this->metadata as $key) {
218 12
            $value = $this->getDataValue($key);
219
220 12
            $metadata[$key] = $value instanceof DateTimeInterface
221 6
                ? $this->serializeDate($value)
222 12
                : $value;
223
        }
224
225 12
        return $json ? json_encode($metadata, $options, $depth) : $metadata;
226
    }
227
228
    /**
229
     * {@inheritdoc}
230
     */
231 24
    public function getModified(bool $json = false, int $options = 0, int $depth = 512)
232
    {
233 24
        if (empty($this->data)) {
234 24
            $this->resolveData();
235
        }
236
237 24
        $modified = [];
238
239 24
        foreach ($this->modified as $key) {
240 21
            $attribute = substr($key, 4);
241 21
            $state = substr($key, 0, 3);
242
243 21
            $value = $this->getDataValue($key);
244
245 21
            $modified[$attribute][$state] = $value instanceof DateTimeInterface
246 6
                ? $this->serializeDate($value)
247 21
                : $value;
248
        }
249
250 24
        return $json ? json_encode($modified, $options, $depth) : $modified;
251
    }
252
253
    /**
254
     * Get the Audit tags as an array.
255
     *
256
     * @return array
257
     */
258 6
    public function getTags(): array
259
    {
260 6
        return preg_split('/,/', $this->tags, null, PREG_SPLIT_NO_EMPTY);
0 ignored issues
show
Bug Best Practice introduced by
The expression return preg_split('/,/',...ng\PREG_SPLIT_NO_EMPTY) could return the type false which is incompatible with the type-hinted return array. Consider adding an additional type-check to rule them out.
Loading history...
261
    }
262
}
263