Passed
Pull Request — master (#680)
by Morten
10:11
created

Audit::getDataValue()   A

Complexity

Conditions 6
Paths 4

Size

Total Lines 25
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 6

Importance

Changes 4
Bugs 0 Features 3
Metric Value
cc 6
eloc 12
c 4
b 0
f 3
nc 4
nop 1
dl 0
loc 25
ccs 12
cts 12
cp 1
crap 6
rs 9.2222
1
<?php
2
3
namespace OwenIt\Auditing;
4
5
use DateTimeInterface;
6
use Illuminate\Database\Eloquent\Model;
7
use Illuminate\Support\Facades\Config;
8
use Illuminate\Support\Str;
9
use OwenIt\Auditing\Contracts\AttributeEncoder;
10
11
trait Audit
12
{
13
    /**
14
     * Audit data.
15
     *
16
     * @var array
17
     */
18
    protected $data = [];
19
20
    /**
21
     * The Audit attributes that belong to the metadata.
22
     *
23
     * @var array
24
     */
25
    protected $metadata = [];
26
27
    /**
28
     * The Auditable attributes that were modified.
29
     *
30
     * @var array
31
     */
32
    protected $modified = [];
33
34
    /**
35
     * {@inheritdoc}
36
     */
37 26
    public function auditable()
38
    {
39 26
        return $this->morphTo();
0 ignored issues
show
Bug introduced by
It seems like morphTo() 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

39
        return $this->/** @scrutinizer ignore-call */ morphTo();
Loading history...
40
    }
41
42
    /**
43
     * {@inheritdoc}
44
     */
45 34
    public function user()
46
    {
47 34
        return $this->morphTo();
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 100
    public function getConnectionName()
54
    {
55 100
        return Config::get('audit.drivers.database.connection');
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 100
    public function getTable(): string
62
    {
63 100
        return Config::get('audit.drivers.database.table', parent::getTable());
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 34
    public function resolveData(): array
70
    {
71 34
        $morphPrefix = Config::get('audit.user.morph_prefix', 'user');
72
73
        // Metadata
74 34
        $this->data = [
75 34
            'audit_id'         => $this->id,
76 34
            'audit_event'      => $this->event,
77 34
            'audit_url'        => $this->url,
78 34
            'audit_ip_address' => $this->ip_address,
79 34
            'audit_user_agent' => $this->user_agent,
80 34
            'audit_tags'       => $this->tags,
81 34
            '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

81
            'audit_created_at' => $this->/** @scrutinizer ignore-call */ serializeDate($this->created_at),
Loading history...
82 34
            'audit_updated_at' => $this->serializeDate($this->updated_at),
83 34
            '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

83
            'user_id'          => $this->/** @scrutinizer ignore-call */ getAttribute($morphPrefix . '_id'),
Loading history...
84 34
            'user_type'        => $this->getAttribute($morphPrefix . '_type'),
85
        ];
86
87 34
        if ($this->user) {
88 22
            foreach ($this->user->getArrayableAttributes() as $attribute => $value) {
89 22
                $this->data['user_' . $attribute] = $value;
90
            }
91
        }
92
93 34
        $this->metadata = array_keys($this->data);
94
95
        // Modified Auditable attributes
96 34
        foreach ($this->new_values as $key => $value) {
97 28
            $this->data['new_' . $key] = $value;
98
        }
99
100 34
        foreach ($this->old_values as $key => $value) {
101 8
            $this->data['old_' . $key] = $value;
102
        }
103
104 34
        $this->modified = array_diff_key(array_keys($this->data), $this->metadata);
105
106 34
        return $this->data;
107
    }
108
109
    /**
110
     * Get the formatted value of an Eloquent model.
111
     *
112
     * @param Model $model
113
     * @param string $key
114
     * @param mixed $value
115
     *
116
     * @return mixed
117
     */
118 22
    protected function getFormattedValue(Model $model, string $key, $value)
119
    {
120
        // Apply defined get mutator
121 22
        if ($model->hasGetMutator($key)) {
122 18
            return $model->mutateAttribute($key, $value);
123
        }
124
125 22
        if (array_key_exists($key, $model->getCasts()) && $model->getCasts()[$key] == 'Illuminate\Database\Eloquent\Casts\AsArrayObject') {
126
            $arrayObject = new \Illuminate\Database\Eloquent\Casts\ArrayObject(json_decode($value, true));
0 ignored issues
show
Bug introduced by
The type Illuminate\Database\Eloquent\Casts\ArrayObject was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
127
            return $arrayObject;
128
        }
129
130
        // Cast to native PHP type
131 22
        if ($model->hasCast($key)) {
132 14
            return $model->castAttribute($key, $value);
133
        }
134
135
        // Honour DateTime attribute
136 20
        if ($value !== null && in_array($key, $model->getDates(), true)) {
137 10
            return $model->asDateTime($value);
138
        }
139
140 20
        return $value;
141
    }
142
143
    /**
144
     * {@inheritdoc}
145
     */
146 26
    public function getDataValue(string $key)
147
    {
148 26
        if (!array_key_exists($key, $this->data)) {
149 2
            return;
150
        }
151
152 26
        $value = $this->data[$key];
153
154
        // User value
155 26
        if ($this->user && Str::startsWith($key, 'user_')) {
156 6
            return $this->getFormattedValue($this->user, substr($key, 5), $value);
157
        }
158
159
        // Auditable value
160 26
        if ($this->auditable && Str::startsWith($key, ['new_', 'old_'])) {
161 18
            $attribute = substr($key, 4);
162
163 18
            return $this->getFormattedValue(
164 18
                $this->auditable,
165
                $attribute,
166 18
                $this->decodeAttributeValue($this->auditable, $attribute, $value)
167
            );
168
        }
169
170 8
        return $value;
171
    }
172
173
    /**
174
     * Decode attribute value.
175
     *
176
     * @param Contracts\Auditable $auditable
177
     * @param string $attribute
178
     * @param mixed $value
179
     *
180
     * @return mixed
181
     */
182 18
    protected function decodeAttributeValue(Contracts\Auditable $auditable, string $attribute, $value)
183
    {
184 18
        $attributeModifiers = $auditable->getAttributeModifiers();
185
186 18
        if (!array_key_exists($attribute, $attributeModifiers)) {
187 18
            return $value;
188
        }
189
190 2
        $attributeDecoder = $attributeModifiers[$attribute];
191
192 2
        if (is_subclass_of($attributeDecoder, AttributeEncoder::class)) {
193 2
            return call_user_func([$attributeDecoder, 'decode'], $value);
194
        }
195
196 2
        return $value;
197
    }
198
199
    /**
200
     * {@inheritdoc}
201
     */
202 8
    public function getMetadata(bool $json = false, int $options = 0, int $depth = 512)
203
    {
204 8
        if (empty($this->data)) {
205 8
            $this->resolveData();
206
        }
207
208 8
        $metadata = [];
209
210 8
        foreach ($this->metadata as $key) {
211 8
            $value = $this->getDataValue($key);
212
213 8
            $metadata[$key] = $value instanceof DateTimeInterface
214 4
                ? $this->serializeDate($value)
215 8
                : $value;
216
        }
217
218 8
        return $json ? json_encode($metadata, $options, $depth) : $metadata;
219
    }
220
221
    /**
222
     * {@inheritdoc}
223
     */
224 20
    public function getModified(bool $json = false, int $options = 0, int $depth = 512)
225
    {
226 20
        if (empty($this->data)) {
227 20
            $this->resolveData();
228
        }
229
230 20
        $modified = [];
231
232 20
        foreach ($this->modified as $key) {
233 16
            $attribute = substr($key, 4);
234 16
            $state = substr($key, 0, 3);
235
236 16
            $value = $this->getDataValue($key);
237
238 16
            $modified[$attribute][$state] = $value instanceof DateTimeInterface
239 4
                ? $this->serializeDate($value)
240 16
                : $value;
241
        }
242
243 20
        return $json ? json_encode($modified, $options, $depth) : $modified;
244
    }
245
246
    /**
247
     * Get the Audit tags as an array.
248
     *
249
     * @return array
250
     */
251 4
    public function getTags(): array
252
    {
253 4
        return preg_split('/,/', $this->tags, null, PREG_SPLIT_NO_EMPTY);
254
    }
255
}
256