Test Failed
Pull Request — master (#684)
by Morten
14:54
created

Audit::getTable()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 1
eloc 1
c 1
b 0
f 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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 2
    public function auditable()
38
    {
39 2
        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 2
    public function user()
46
    {
47 2
        return $this->morphTo();
48
    }
49
50
    /**
51
     * {@inheritdoc}
52
     */
53 36
    public function getConnectionName()
54
    {
55 36
        return Config::get('audit.drivers.database.connection');
56
    }
57
58
    /**
59
     * {@inheritdoc}
60
     */
61 36
    public function getTable(): string
62
    {
63 36
        return Config::get('audit.drivers.database.table', parent::getTable());
64
    }
65
66
    /**
67
     * {@inheritdoc}
68
     */
69 2
    public function resolveData(): array
70
    {
71 2
        $morphPrefix = Config::get('audit.user.morph_prefix', 'user');
72
73
        // Metadata
74 2
        $this->data = [
75 2
            'audit_id'         => $this->id,
76 2
            'audit_event'      => $this->event,
77 2
            'audit_tags'       => $this->tags,
78 2
            '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

78
            'audit_created_at' => $this->/** @scrutinizer ignore-call */ serializeDate($this->created_at),
Loading history...
79 2
            'audit_updated_at' => $this->serializeDate($this->updated_at),
80 2
            '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

80
            'user_id'          => $this->/** @scrutinizer ignore-call */ getAttribute($morphPrefix . '_id'),
Loading history...
81 2
            'user_type'        => $this->getAttribute($morphPrefix . '_type'),
82
        ];
83
84
        // add resolvers data to metadata
85 2
        $resolverData = [];
86 2
        foreach (array_keys(Config::get('audit.resolvers', [])) as $name) {
87 2
            $resolverData['audit_' . $name] = $this->$name;
88
        }
89 2
        $this->data = array_merge($this->data, $resolverData);
90
91 2
        if ($this->user) {
92
            foreach ($this->user->getArrayableAttributes() as $attribute => $value) {
93
                $this->data['user_' . $attribute] = $value;
94
            }
95
        }
96
97 2
        $this->metadata = array_keys($this->data);
98
99
        // Modified Auditable attributes
100 2
        foreach ($this->new_values as $key => $value) {
101 2
            $this->data['new_' . $key] = $value;
102
        }
103
104 2
        foreach ($this->old_values as $key => $value) {
105 2
            $this->data['old_' . $key] = $value;
106
        }
107
108 2
        $this->modified = array_diff_key(array_keys($this->data), $this->metadata);
109
110 2
        return $this->data;
111
    }
112
113
    /**
114
     * Get the formatted value of an Eloquent model.
115
     *
116
     * @param Model $model
117
     * @param string $key
118
     * @param mixed $value
119
     *
120
     * @return mixed
121
     */
122 2
    protected function getFormattedValue(Model $model, string $key, $value)
123
    {
124
        // Apply defined get mutator
125 2
        if ($model->hasGetMutator($key)) {
126
            return $model->mutateAttribute($key, $value);
127
        }
128
129 2
        if (array_key_exists(
130 2
            $key,
131 2
            $model->getCasts()
132 2
        ) && $model->getCasts()[$key] == 'Illuminate\Database\Eloquent\Casts\AsArrayObject') {
133 2
            $arrayObject = new \Illuminate\Database\Eloquent\Casts\ArrayObject(json_decode($value, true));
134 2
            return $arrayObject;
135
        }
136
137
        // Cast to native PHP type
138
        if ($model->hasCast($key)) {
139
            return $model->castAttribute($key, $value);
140
        }
141
142
        // Honour DateTime attribute
143
        if ($value !== null && in_array($key, $model->getDates(), true)) {
144
            return $model->asDateTime($value);
145
        }
146
147
        return $value;
148
    }
149
150
    /**
151
     * {@inheritdoc}
152
     */
153 2
    public function getDataValue(string $key)
154
    {
155 2
        if (!array_key_exists($key, $this->data)) {
156
            return;
157
        }
158
159 2
        $value = $this->data[$key];
160
161
        // User value
162 2
        if ($this->user && Str::startsWith($key, 'user_')) {
163
            return $this->getFormattedValue($this->user, substr($key, 5), $value);
164
        }
165
166
        // Auditable value
167 2
        if ($this->auditable && Str::startsWith($key, ['new_', 'old_'])) {
168 2
            $attribute = substr($key, 4);
169
170 2
            return $this->getFormattedValue(
171 2
                $this->auditable,
172
                $attribute,
173 2
                $this->decodeAttributeValue($this->auditable, $attribute, $value)
174
            );
175
        }
176
177
        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 2
    protected function decodeAttributeValue(Contracts\Auditable $auditable, string $attribute, $value)
190
    {
191 2
        $attributeModifiers = $auditable->getAttributeModifiers();
192
193 2
        if (!array_key_exists($attribute, $attributeModifiers)) {
194 2
            return $value;
195
        }
196
197
        $attributeDecoder = $attributeModifiers[$attribute];
198
199
        if (is_subclass_of($attributeDecoder, AttributeEncoder::class)) {
200
            return call_user_func([$attributeDecoder, 'decode'], $value);
201
        }
202
203
        return $value;
204
    }
205
206
    /**
207
     * {@inheritdoc}
208
     */
209
    public function getMetadata(bool $json = false, int $options = 0, int $depth = 512)
210
    {
211
        if (empty($this->data)) {
212
            $this->resolveData();
213
        }
214
215
        $metadata = [];
216
217
        foreach ($this->metadata as $key) {
218
            $value = $this->getDataValue($key);
219
            $metadata[$key] = $value;
220
221
            if ($value instanceof DateTimeInterface) {
222
                $metadata[$key] = !is_null($this->auditable) ? $this->auditable->serializeDate($value) : $this->serializeDate($value);
223
            }
224
        }
225
226
        return $json ? json_encode($metadata, $options, $depth) : $metadata;
227
    }
228
229
    /**
230
     * {@inheritdoc}
231
     */
232 2
    public function getModified(bool $json = false, int $options = 0, int $depth = 512)
233
    {
234 2
        if (empty($this->data)) {
235 2
            $this->resolveData();
236
        }
237
238 2
        $modified = [];
239
240 2
        foreach ($this->modified as $key) {
241 2
            $attribute = substr($key, 4);
242 2
            $state = substr($key, 0, 3);
243
244 2
            $value = $this->getDataValue($key);
245 2
            $modified[$attribute][$state] = $value;
246
247 2
            if ($value instanceof DateTimeInterface) {
248
                $modified[$attribute][$state] = !is_null($this->auditable) ? $this->auditable->serializeDate($value) : $this->serializeDate($value);
249
            }
250
        }
251
252 2
        return $json ? json_encode($modified, $options, $depth) : $modified;
253
    }
254
255
    /**
256
     * Get the Audit tags as an array.
257
     *
258
     * @return array
259
     */
260
    public function getTags(): array
261
    {
262
        return preg_split('/,/', $this->tags, null, PREG_SPLIT_NO_EMPTY);
263
    }
264
}
265