1 | <?php |
||||||
2 | |||||||
3 | namespace OwenIt\Auditing; |
||||||
4 | |||||||
5 | use Illuminate\Database\Eloquent\Relations\MorphMany; |
||||||
6 | use Illuminate\Database\Eloquent\SoftDeletes; |
||||||
7 | use Illuminate\Support\Arr; |
||||||
8 | use Illuminate\Support\Facades\App; |
||||||
9 | use Illuminate\Support\Facades\Config; |
||||||
10 | use OwenIt\Auditing\Contracts\AttributeEncoder; |
||||||
11 | use OwenIt\Auditing\Contracts\AttributeRedactor; |
||||||
12 | use OwenIt\Auditing\Contracts\IpAddressResolver; |
||||||
13 | use OwenIt\Auditing\Contracts\UrlResolver; |
||||||
14 | use OwenIt\Auditing\Contracts\UserAgentResolver; |
||||||
15 | use OwenIt\Auditing\Contracts\UserResolver; |
||||||
16 | use OwenIt\Auditing\Exceptions\AuditableTransitionException; |
||||||
17 | use OwenIt\Auditing\Exceptions\AuditingException; |
||||||
18 | |||||||
19 | trait Auditable |
||||||
20 | { |
||||||
21 | /** |
||||||
22 | * Auditable attributes excluded from the Audit. |
||||||
23 | * |
||||||
24 | * @var array |
||||||
25 | */ |
||||||
26 | protected $excludedAttributes = []; |
||||||
27 | |||||||
28 | /** |
||||||
29 | * Audit event name. |
||||||
30 | * |
||||||
31 | * @var string |
||||||
32 | */ |
||||||
33 | protected $auditEvent; |
||||||
34 | |||||||
35 | /** |
||||||
36 | * Is auditing disabled? |
||||||
37 | * |
||||||
38 | * @var bool |
||||||
39 | */ |
||||||
40 | public static $auditingDisabled = false; |
||||||
41 | |||||||
42 | /** |
||||||
43 | * Auditable boot logic. |
||||||
44 | * |
||||||
45 | * @return void |
||||||
46 | */ |
||||||
47 | 92 | public static function bootAuditable() |
|||||
48 | { |
||||||
49 | 92 | if (!self::$auditingDisabled && static::isAuditingEnabled()) { |
|||||
50 | 90 | static::observe(new AuditableObserver()); |
|||||
51 | } |
||||||
52 | 92 | } |
|||||
53 | |||||||
54 | /** |
||||||
55 | * {@inheritdoc} |
||||||
56 | */ |
||||||
57 | 13 | public function audits(): MorphMany |
|||||
58 | { |
||||||
59 | 13 | return $this->morphMany( |
|||||
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||||||
60 | 13 | Config::get('audit.implementation', Models\Audit::class), |
|||||
61 | 13 | 'auditable' |
|||||
62 | ); |
||||||
63 | } |
||||||
64 | |||||||
65 | /** |
||||||
66 | * Resolve the Auditable attributes to exclude from the Audit. |
||||||
67 | * |
||||||
68 | * @return void |
||||||
69 | */ |
||||||
70 | 56 | protected function resolveAuditExclusions() |
|||||
71 | { |
||||||
72 | 56 | $this->excludedAttributes = $this->getAuditExclude(); |
|||||
73 | |||||||
74 | // When in strict mode, hidden and non visible attributes are excluded |
||||||
75 | 56 | if ($this->getAuditStrict()) { |
|||||
76 | // Hidden attributes |
||||||
77 | 1 | $this->excludedAttributes = array_merge($this->excludedAttributes, $this->hidden); |
|||||
78 | |||||||
79 | // Non visible attributes |
||||||
80 | 1 | if ($this->visible) { |
|||||
81 | 1 | $invisible = array_diff(array_keys($this->attributes), $this->visible); |
|||||
82 | |||||||
83 | 1 | $this->excludedAttributes = array_merge($this->excludedAttributes, $invisible); |
|||||
84 | } |
||||||
85 | } |
||||||
86 | |||||||
87 | // Exclude Timestamps |
||||||
88 | 56 | if (!$this->getAuditTimestamps()) { |
|||||
89 | 56 | array_push($this->excludedAttributes, $this->getCreatedAtColumn(), $this->getUpdatedAtColumn()); |
|||||
0 ignored issues
–
show
It seems like
getCreatedAtColumn() 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
![]() It seems like
getUpdatedAtColumn() 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
![]() |
|||||||
90 | |||||||
91 | 56 | if (in_array(SoftDeletes::class, class_uses_recursive(get_class($this)))) { |
|||||
92 | 52 | $this->excludedAttributes[] = $this->getDeletedAtColumn(); |
|||||
0 ignored issues
–
show
It seems like
getDeletedAtColumn() 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
![]() |
|||||||
93 | } |
||||||
94 | } |
||||||
95 | |||||||
96 | // Valid attributes are all those that made it out of the exclusion array |
||||||
97 | 56 | $attributes = Arr::except($this->attributes, $this->excludedAttributes); |
|||||
98 | |||||||
99 | 56 | foreach ($attributes as $attribute => $value) { |
|||||
100 | // Apart from null, non scalar values will be excluded |
||||||
101 | 52 | if (is_array($value) || (is_object($value) && !method_exists($value, '__toString'))) { |
|||||
102 | 52 | $this->excludedAttributes[] = $attribute; |
|||||
103 | } |
||||||
104 | } |
||||||
105 | 56 | } |
|||||
106 | |||||||
107 | /** |
||||||
108 | * Get the old/new attributes of a retrieved event. |
||||||
109 | * |
||||||
110 | * @return array |
||||||
111 | */ |
||||||
112 | 2 | protected function getRetrievedEventAttributes(): array |
|||||
113 | { |
||||||
114 | // This is a read event with no attribute changes, |
||||||
115 | // only metadata will be stored in the Audit |
||||||
116 | |||||||
117 | return [ |
||||||
118 | 2 | [], |
|||||
119 | [], |
||||||
120 | ]; |
||||||
121 | } |
||||||
122 | |||||||
123 | /** |
||||||
124 | * Get the old/new attributes of a created event. |
||||||
125 | * |
||||||
126 | * @return array |
||||||
127 | */ |
||||||
128 | 49 | protected function getCreatedEventAttributes(): array |
|||||
129 | { |
||||||
130 | 49 | $new = []; |
|||||
131 | |||||||
132 | 49 | foreach ($this->attributes as $attribute => $value) { |
|||||
133 | 45 | if ($this->isAttributeAuditable($attribute)) { |
|||||
134 | 45 | $new[$attribute] = $value; |
|||||
135 | } |
||||||
136 | } |
||||||
137 | |||||||
138 | return [ |
||||||
139 | 49 | [], |
|||||
140 | 49 | $new, |
|||||
141 | ]; |
||||||
142 | } |
||||||
143 | |||||||
144 | /** |
||||||
145 | * Get the old/new attributes of an updated event. |
||||||
146 | * |
||||||
147 | * @return array |
||||||
148 | */ |
||||||
149 | 6 | protected function getUpdatedEventAttributes(): array |
|||||
150 | { |
||||||
151 | 6 | $old = []; |
|||||
152 | 6 | $new = []; |
|||||
153 | |||||||
154 | 6 | foreach ($this->getDirty() as $attribute => $value) { |
|||||
0 ignored issues
–
show
It seems like
getDirty() 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
![]() |
|||||||
155 | 5 | if ($this->isAttributeAuditable($attribute)) { |
|||||
156 | 5 | $old[$attribute] = Arr::get($this->original, $attribute); |
|||||
157 | 5 | $new[$attribute] = Arr::get($this->attributes, $attribute); |
|||||
158 | } |
||||||
159 | } |
||||||
160 | |||||||
161 | return [ |
||||||
162 | 6 | $old, |
|||||
163 | 6 | $new, |
|||||
164 | ]; |
||||||
165 | } |
||||||
166 | |||||||
167 | /** |
||||||
168 | * Get the old/new attributes of a deleted event. |
||||||
169 | * |
||||||
170 | * @return array |
||||||
171 | */ |
||||||
172 | 4 | protected function getDeletedEventAttributes(): array |
|||||
173 | { |
||||||
174 | 4 | $old = []; |
|||||
175 | |||||||
176 | 4 | foreach ($this->attributes as $attribute => $value) { |
|||||
177 | 4 | if ($this->isAttributeAuditable($attribute)) { |
|||||
178 | 4 | $old[$attribute] = $value; |
|||||
179 | } |
||||||
180 | } |
||||||
181 | |||||||
182 | return [ |
||||||
183 | 4 | $old, |
|||||
184 | [], |
||||||
185 | ]; |
||||||
186 | } |
||||||
187 | |||||||
188 | /** |
||||||
189 | * Get the old/new attributes of a restored event. |
||||||
190 | * |
||||||
191 | * @return array |
||||||
192 | */ |
||||||
193 | 2 | protected function getRestoredEventAttributes(): array |
|||||
194 | { |
||||||
195 | // A restored event is just a deleted event in reverse |
||||||
196 | 2 | return array_reverse($this->getDeletedEventAttributes()); |
|||||
197 | } |
||||||
198 | |||||||
199 | /** |
||||||
200 | * {@inheritdoc} |
||||||
201 | */ |
||||||
202 | 67 | public function readyForAuditing(): bool |
|||||
203 | { |
||||||
204 | 67 | if (static::$auditingDisabled) { |
|||||
205 | 1 | return false; |
|||||
206 | } |
||||||
207 | |||||||
208 | 67 | return $this->isEventAuditable($this->auditEvent); |
|||||
209 | } |
||||||
210 | |||||||
211 | /** |
||||||
212 | * Modify attribute value. |
||||||
213 | * |
||||||
214 | * @param string $attribute |
||||||
215 | * @param mixed $value |
||||||
216 | * |
||||||
217 | * @throws AuditingException |
||||||
218 | * |
||||||
219 | * @return mixed |
||||||
220 | */ |
||||||
221 | 2 | protected function modifyAttributeValue(string $attribute, $value) |
|||||
222 | { |
||||||
223 | 2 | $attributeModifiers = $this->getAttributeModifiers(); |
|||||
224 | |||||||
225 | 2 | if (!array_key_exists($attribute, $attributeModifiers)) { |
|||||
226 | 1 | return $value; |
|||||
227 | } |
||||||
228 | |||||||
229 | 2 | $attributeModifier = $attributeModifiers[$attribute]; |
|||||
230 | |||||||
231 | 2 | if (is_subclass_of($attributeModifier, AttributeRedactor::class)) { |
|||||
232 | 1 | return call_user_func([$attributeModifier, 'redact'], $value); |
|||||
233 | } |
||||||
234 | |||||||
235 | 2 | if (is_subclass_of($attributeModifier, AttributeEncoder::class)) { |
|||||
236 | 1 | return call_user_func([$attributeModifier, 'encode'], $value); |
|||||
237 | } |
||||||
238 | |||||||
239 | 1 | throw new AuditingException(sprintf('Invalid AttributeModifier implementation: %s', $attributeModifier)); |
|||||
240 | } |
||||||
241 | |||||||
242 | /** |
||||||
243 | * {@inheritdoc} |
||||||
244 | */ |
||||||
245 | 61 | public function toAudit(): array |
|||||
246 | { |
||||||
247 | 61 | if (!$this->readyForAuditing()) { |
|||||
248 | 1 | throw new AuditingException('A valid audit event has not been set'); |
|||||
249 | } |
||||||
250 | |||||||
251 | 60 | $attributeGetter = $this->resolveAttributeGetter($this->auditEvent); |
|||||
252 | |||||||
253 | 60 | if (!method_exists($this, $attributeGetter)) { |
|||||
254 | 4 | throw new AuditingException(sprintf( |
|||||
255 | 4 | 'Unable to handle "%s" event, %s() method missing', |
|||||
256 | 4 | $this->auditEvent, |
|||||
257 | 4 | $attributeGetter |
|||||
258 | )); |
||||||
259 | } |
||||||
260 | |||||||
261 | 56 | $this->resolveAuditExclusions(); |
|||||
262 | |||||||
263 | 56 | list($old, $new) = $this->$attributeGetter(); |
|||||
264 | |||||||
265 | 56 | if ($this->getAttributeModifiers()) { |
|||||
266 | 2 | foreach ($old as $attribute => $value) { |
|||||
267 | 1 | $old[$attribute] = $this->modifyAttributeValue($attribute, $value); |
|||||
268 | } |
||||||
269 | |||||||
270 | 2 | foreach ($new as $attribute => $value) { |
|||||
271 | 2 | $new[$attribute] = $this->modifyAttributeValue($attribute, $value); |
|||||
272 | } |
||||||
273 | } |
||||||
274 | |||||||
275 | 55 | $morphPrefix = Config::get('audit.user.morph_prefix', 'user'); |
|||||
276 | |||||||
277 | 55 | $tags = implode(',', $this->generateTags()); |
|||||
278 | |||||||
279 | 55 | $user = $this->resolveUser(); |
|||||
280 | |||||||
281 | 54 | return $this->transformAudit([ |
|||||
282 | 54 | 'old_values' => $old, |
|||||
283 | 54 | 'new_values' => $new, |
|||||
284 | 54 | 'event' => $this->auditEvent, |
|||||
285 | 54 | 'auditable_id' => $this->getKey(), |
|||||
0 ignored issues
–
show
It seems like
getKey() 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
![]() |
|||||||
286 | 54 | 'auditable_type' => $this->getMorphClass(), |
|||||
0 ignored issues
–
show
It seems like
getMorphClass() 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
![]() |
|||||||
287 | 54 | $morphPrefix . '_id' => $user ? $user->getAuthIdentifier() : null, |
|||||
288 | 54 | $morphPrefix . '_type' => $user ? $user->getMorphClass() : null, |
|||||
289 | 54 | 'url' => $this->resolveUrl(), |
|||||
290 | 53 | 'ip_address' => $this->resolveIpAddress(), |
|||||
291 | 52 | 'user_agent' => $this->resolveUserAgent(), |
|||||
292 | 51 | 'tags' => empty($tags) ? null : $tags, |
|||||
293 | ]); |
||||||
294 | } |
||||||
295 | |||||||
296 | /** |
||||||
297 | * {@inheritdoc} |
||||||
298 | */ |
||||||
299 | 50 | public function transformAudit(array $data): array |
|||||
300 | { |
||||||
301 | 50 | return $data; |
|||||
302 | } |
||||||
303 | |||||||
304 | /** |
||||||
305 | * Resolve the User. |
||||||
306 | * |
||||||
307 | * @throws AuditingException |
||||||
308 | * |
||||||
309 | * @return mixed|null |
||||||
310 | */ |
||||||
311 | 55 | protected function resolveUser() |
|||||
312 | { |
||||||
313 | 55 | $userResolver = Config::get('audit.resolver.user'); |
|||||
314 | |||||||
315 | 55 | if (is_subclass_of($userResolver, UserResolver::class)) { |
|||||
316 | 54 | return call_user_func([$userResolver, 'resolve']); |
|||||
317 | } |
||||||
318 | |||||||
319 | 1 | throw new AuditingException('Invalid UserResolver implementation'); |
|||||
320 | } |
||||||
321 | |||||||
322 | /** |
||||||
323 | * Resolve the URL. |
||||||
324 | * |
||||||
325 | * @throws AuditingException |
||||||
326 | * |
||||||
327 | * @return string |
||||||
328 | */ |
||||||
329 | 54 | protected function resolveUrl(): string |
|||||
330 | { |
||||||
331 | 54 | $urlResolver = Config::get('audit.resolver.url'); |
|||||
332 | |||||||
333 | 54 | if (is_subclass_of($urlResolver, UrlResolver::class)) { |
|||||
334 | 53 | return call_user_func([$urlResolver, 'resolve']); |
|||||
335 | } |
||||||
336 | |||||||
337 | 1 | throw new AuditingException('Invalid UrlResolver implementation'); |
|||||
338 | } |
||||||
339 | |||||||
340 | /** |
||||||
341 | * Resolve the IP Address. |
||||||
342 | * |
||||||
343 | * @throws AuditingException |
||||||
344 | * |
||||||
345 | * @return string |
||||||
346 | */ |
||||||
347 | 53 | protected function resolveIpAddress(): string |
|||||
348 | { |
||||||
349 | 53 | $ipAddressResolver = Config::get('audit.resolver.ip_address'); |
|||||
350 | |||||||
351 | 53 | if (is_subclass_of($ipAddressResolver, IpAddressResolver::class)) { |
|||||
352 | 52 | return call_user_func([$ipAddressResolver, 'resolve']); |
|||||
353 | } |
||||||
354 | |||||||
355 | 1 | throw new AuditingException('Invalid IpAddressResolver implementation'); |
|||||
356 | } |
||||||
357 | |||||||
358 | /** |
||||||
359 | * Resolve the User Agent. |
||||||
360 | * |
||||||
361 | * @throws AuditingException |
||||||
362 | * |
||||||
363 | * @return string|null |
||||||
364 | */ |
||||||
365 | 52 | protected function resolveUserAgent() |
|||||
366 | { |
||||||
367 | 52 | $userAgentResolver = Config::get('audit.resolver.user_agent'); |
|||||
368 | |||||||
369 | 52 | if (is_subclass_of($userAgentResolver, UserAgentResolver::class)) { |
|||||
370 | 51 | return call_user_func([$userAgentResolver, 'resolve']); |
|||||
371 | } |
||||||
372 | |||||||
373 | 1 | throw new AuditingException('Invalid UserAgentResolver implementation'); |
|||||
374 | } |
||||||
375 | |||||||
376 | /** |
||||||
377 | * Determine if an attribute is eligible for auditing. |
||||||
378 | * |
||||||
379 | * @param string $attribute |
||||||
380 | * |
||||||
381 | * @return bool |
||||||
382 | */ |
||||||
383 | 51 | protected function isAttributeAuditable(string $attribute): bool |
|||||
384 | { |
||||||
385 | // The attribute should not be audited |
||||||
386 | 51 | if (in_array($attribute, $this->excludedAttributes, true)) { |
|||||
387 | 48 | return false; |
|||||
388 | } |
||||||
389 | |||||||
390 | // The attribute is auditable when explicitly |
||||||
391 | // listed or when the include array is empty |
||||||
392 | 51 | $include = $this->getAuditInclude(); |
|||||
393 | |||||||
394 | 51 | return empty($include) || in_array($attribute, $include, true); |
|||||
395 | } |
||||||
396 | |||||||
397 | /** |
||||||
398 | * Determine whether an event is auditable. |
||||||
399 | * |
||||||
400 | * @param string $event |
||||||
401 | * |
||||||
402 | * @return bool |
||||||
403 | */ |
||||||
404 | 68 | protected function isEventAuditable($event): bool |
|||||
405 | { |
||||||
406 | 68 | return is_string($this->resolveAttributeGetter($event)); |
|||||
407 | } |
||||||
408 | |||||||
409 | /** |
||||||
410 | * Attribute getter method resolver. |
||||||
411 | * |
||||||
412 | * @param string $event |
||||||
413 | * |
||||||
414 | * @return string|null |
||||||
415 | */ |
||||||
416 | 68 | protected function resolveAttributeGetter($event) |
|||||
417 | { |
||||||
418 | 68 | foreach ($this->getAuditEvents() as $key => $value) { |
|||||
419 | 68 | $auditableEvent = is_int($key) ? $value : $key; |
|||||
420 | |||||||
421 | 68 | $auditableEventRegex = sprintf('/%s/', preg_replace('/\*+/', '.*', $auditableEvent)); |
|||||
422 | |||||||
423 | 68 | if (preg_match($auditableEventRegex, $event)) { |
|||||
424 | 68 | return is_int($key) ? sprintf('get%sEventAttributes', ucfirst($event)) : $value; |
|||||
425 | } |
||||||
426 | } |
||||||
427 | 26 | } |
|||||
428 | |||||||
429 | /** |
||||||
430 | * {@inheritdoc} |
||||||
431 | */ |
||||||
432 | 68 | public function setAuditEvent(string $event): Contracts\Auditable |
|||||
433 | { |
||||||
434 | 68 | $this->auditEvent = $this->isEventAuditable($event) ? $event : null; |
|||||
435 | |||||||
436 | 68 | return $this; |
|||||
0 ignored issues
–
show
|
|||||||
437 | } |
||||||
438 | |||||||
439 | /** |
||||||
440 | * {@inheritdoc} |
||||||
441 | */ |
||||||
442 | 2 | public function getAuditEvent() |
|||||
443 | { |
||||||
444 | 2 | return $this->auditEvent; |
|||||
445 | } |
||||||
446 | |||||||
447 | /** |
||||||
448 | * {@inheritdoc} |
||||||
449 | */ |
||||||
450 | 71 | public function getAuditEvents(): array |
|||||
451 | { |
||||||
452 | 71 | return $this->auditEvents ?? Config::get('audit.events', [ |
|||||
0 ignored issues
–
show
|
|||||||
453 | 71 | 'created', |
|||||
454 | 'updated', |
||||||
455 | 'deleted', |
||||||
456 | 'restored', |
||||||
457 | ]); |
||||||
458 | } |
||||||
459 | |||||||
460 | /** |
||||||
461 | * Disable Auditing. |
||||||
462 | * |
||||||
463 | * @return void |
||||||
464 | */ |
||||||
465 | 1 | public static function disableAuditing() |
|||||
466 | { |
||||||
467 | 1 | static::$auditingDisabled = true; |
|||||
468 | 1 | } |
|||||
469 | |||||||
470 | /** |
||||||
471 | * Enable Auditing. |
||||||
472 | * |
||||||
473 | * @return void |
||||||
474 | */ |
||||||
475 | 1 | public static function enableAuditing() |
|||||
476 | { |
||||||
477 | 1 | static::$auditingDisabled = false; |
|||||
478 | 1 | } |
|||||
479 | |||||||
480 | /** |
||||||
481 | * Determine whether auditing is enabled. |
||||||
482 | * |
||||||
483 | * @return bool |
||||||
484 | */ |
||||||
485 | 94 | public static function isAuditingEnabled(): bool |
|||||
486 | { |
||||||
487 | 94 | if (App::runningInConsole()) { |
|||||
488 | 92 | return Config::get('audit.console', false); |
|||||
489 | } |
||||||
490 | |||||||
491 | 2 | return Config::get('audit.enabled', true); |
|||||
492 | } |
||||||
493 | |||||||
494 | /** |
||||||
495 | * {@inheritdoc} |
||||||
496 | */ |
||||||
497 | 53 | public function getAuditInclude(): array |
|||||
498 | { |
||||||
499 | 53 | return $this->auditInclude ?? []; |
|||||
500 | } |
||||||
501 | |||||||
502 | /** |
||||||
503 | * {@inheritdoc} |
||||||
504 | */ |
||||||
505 | 58 | public function getAuditExclude(): array |
|||||
506 | { |
||||||
507 | 58 | return $this->auditExclude ?? []; |
|||||
508 | } |
||||||
509 | |||||||
510 | /** |
||||||
511 | * {@inheritdoc} |
||||||
512 | */ |
||||||
513 | 59 | public function getAuditStrict(): bool |
|||||
514 | { |
||||||
515 | 59 | return $this->auditStrict ?? Config::get('audit.strict', false); |
|||||
516 | } |
||||||
517 | |||||||
518 | /** |
||||||
519 | * {@inheritdoc} |
||||||
520 | */ |
||||||
521 | 59 | public function getAuditTimestamps(): bool |
|||||
522 | { |
||||||
523 | 59 | return $this->auditTimestamps ?? Config::get('audit.timestamps', false); |
|||||
524 | } |
||||||
525 | |||||||
526 | /** |
||||||
527 | * {@inheritdoc} |
||||||
528 | */ |
||||||
529 | 53 | public function getAuditDriver() |
|||||
530 | { |
||||||
531 | 53 | return $this->auditDriver ?? Config::get('audit.driver', 'database'); |
|||||
532 | } |
||||||
533 | |||||||
534 | /** |
||||||
535 | * {@inheritdoc} |
||||||
536 | */ |
||||||
537 | 50 | public function getAuditThreshold(): int |
|||||
538 | { |
||||||
539 | 50 | return $this->auditThreshold ?? Config::get('audit.threshold', 0); |
|||||
540 | } |
||||||
541 | |||||||
542 | /** |
||||||
543 | * {@inheritdoc} |
||||||
544 | */ |
||||||
545 | 56 | public function getAttributeModifiers(): array |
|||||
546 | { |
||||||
547 | 56 | return $this->attributeModifiers ?? []; |
|||||
548 | } |
||||||
549 | |||||||
550 | /** |
||||||
551 | * {@inheritdoc} |
||||||
552 | */ |
||||||
553 | 56 | public function generateTags(): array |
|||||
554 | { |
||||||
555 | 56 | return []; |
|||||
556 | } |
||||||
557 | |||||||
558 | /** |
||||||
559 | * {@inheritdoc} |
||||||
560 | */ |
||||||
561 | 11 | public function transitionTo(Contracts\Audit $audit, bool $old = false): Contracts\Auditable |
|||||
562 | { |
||||||
563 | // The Audit must be for an Auditable model of this type |
||||||
564 | 11 | if ($this->getMorphClass() !== $audit->auditable_type) { |
|||||
0 ignored issues
–
show
|
|||||||
565 | 2 | throw new AuditableTransitionException(sprintf( |
|||||
566 | 2 | 'Expected Auditable type %s, got %s instead', |
|||||
567 | 2 | $this->getMorphClass(), |
|||||
568 | 2 | $audit->auditable_type |
|||||
569 | )); |
||||||
570 | } |
||||||
571 | |||||||
572 | // The Audit must be for this specific Auditable model |
||||||
573 | 9 | if ($this->getKey() !== $audit->auditable_id) { |
|||||
0 ignored issues
–
show
|
|||||||
574 | 2 | throw new AuditableTransitionException(sprintf( |
|||||
575 | 2 | 'Expected Auditable id %s, got %s instead', |
|||||
576 | 2 | $this->getKey(), |
|||||
577 | 2 | $audit->auditable_id |
|||||
578 | )); |
||||||
579 | } |
||||||
580 | |||||||
581 | // Redacted data should not be used when transitioning states |
||||||
582 | 7 | foreach ($this->getAttributeModifiers() as $attribute => $modifier) { |
|||||
583 | 1 | if (is_subclass_of($modifier, AttributeRedactor::class)) { |
|||||
584 | 1 | throw new AuditableTransitionException('Cannot transition states when an AttributeRedactor is set'); |
|||||
585 | } |
||||||
586 | } |
||||||
587 | |||||||
588 | // The attribute compatibility between the Audit and the Auditable model must be met |
||||||
589 | 6 | $modified = $audit->getModified(); |
|||||
590 | |||||||
591 | 6 | if ($incompatibilities = array_diff_key($modified, $this->getAttributes())) { |
|||||
0 ignored issues
–
show
The method
getAttributes() does not exist on OwenIt\Auditing\Auditable . Did you maybe mean getAttributeModifiers() ?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed. ![]() It seems like
$modified can also be of type string ; however, parameter $array1 of array_diff_key() does only seem to accept array , maybe add an additional type check?
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
592 | 1 | throw new AuditableTransitionException(sprintf( |
|||||
593 | 1 | 'Incompatibility between [%s:%s] and [%s:%s]', |
|||||
594 | 1 | $this->getMorphClass(), |
|||||
595 | 1 | $this->getKey(), |
|||||
596 | 1 | get_class($audit), |
|||||
597 | 1 | $audit->getKey() |
|||||
0 ignored issues
–
show
The method
getKey() does not exist on OwenIt\Auditing\Contracts\Audit . Since it exists in all sub-types, consider adding an abstract or default implementation to OwenIt\Auditing\Contracts\Audit .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
![]() |
|||||||
598 | 1 | ), array_keys($incompatibilities)); |
|||||
599 | } |
||||||
600 | |||||||
601 | 5 | $key = $old ? 'old' : 'new'; |
|||||
602 | |||||||
603 | 5 | foreach ($modified as $attribute => $value) { |
|||||
604 | 3 | if (array_key_exists($key, $value)) { |
|||||
605 | 3 | $this->setAttribute($attribute, $value[$key]); |
|||||
0 ignored issues
–
show
It seems like
setAttribute() 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
![]() |
|||||||
606 | } |
||||||
607 | } |
||||||
608 | |||||||
609 | 5 | return $this; |
|||||
0 ignored issues
–
show
|
|||||||
610 | } |
||||||
611 | } |
||||||
612 |