Completed
Push — master ( 50d20d...cb715f )
by Tom
12s queued 10s
created

src/Traits/DetectsChanges.php (1 issue)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
3
namespace Spatie\Activitylog\Traits;
4
5
use Illuminate\Support\Str;
6
use Illuminate\Database\Eloquent\Model;
7
use Spatie\Activitylog\Exceptions\CouldNotLogChanges;
8
9
trait DetectsChanges
10
{
11
    protected $oldAttributes = [];
12
13 172
    protected static function bootDetectsChanges()
14
    {
15 172
        if (static::eventsToBeRecorded()->contains('updated')) {
16
            static::updating(function (Model $model) {
17
18
                //temporary hold the original attributes on the model
19
                //as we'll need these in the updating event
20 76
                $oldValues = (new static)->setRawAttributes($model->getOriginal());
21
22 76
                $model->oldAttributes = static::logChanges($oldValues);
23 172
            });
24
        }
25 172
    }
26
27 164
    public function attributesToBeLogged(): array
28
    {
29 164
        $attributes = [];
30
31 164
        if (isset(static::$logFillable) && static::$logFillable) {
32 8
            $attributes = array_merge($attributes, $this->getFillable());
33
        }
34
35 164
        if ($this->shouldLogUnguarded()) {
36 4
            $attributes = array_merge($attributes, array_diff(array_keys($this->getAttributes()), $this->getGuarded()));
37
        }
38
39 164
        if (isset(static::$logAttributes) && is_array(static::$logAttributes)) {
40 96
            $attributes = array_merge($attributes, array_diff(static::$logAttributes, ['*']));
41
42 96
            if (in_array('*', static::$logAttributes)) {
43 24
                $attributes = array_merge($attributes, array_keys($this->getAttributes()));
44
            }
45
        }
46
47 164
        if (isset(static::$logAttributesToIgnore) && is_array(static::$logAttributesToIgnore)) {
48 8
            $attributes = array_diff($attributes, static::$logAttributesToIgnore);
49
        }
50
51 164
        return $attributes;
52
    }
53
54 100
    public function shouldLogOnlyDirty(): bool
55
    {
56 100
        if (! isset(static::$logOnlyDirty)) {
57 76
            return false;
58
        }
59
60 24
        return static::$logOnlyDirty;
61
    }
62
63 164
    public function shouldLogUnguarded(): bool
64
    {
65 164
        if (! isset(static::$logUnguarded)) {
66 156
            return false;
67
        }
68
69 8
        if (! static::$logUnguarded) {
70
            return false;
71
        }
72
73 8
        if (in_array('*', $this->getGuarded())) {
74 4
            return false;
75
        }
76
77 4
        return true;
78
    }
79
80 164
    public function attributeValuesToBeLogged(string $processingEvent): array
81
    {
82 164
        if (! count($this->attributesToBeLogged())) {
83 64
            return [];
84
        }
85
86 100
        $properties['attributes'] = static::logChanges(
0 ignored issues
show
Coding Style Comprehensibility introduced by
$properties was never initialized. Although not strictly required by PHP, it is generally a good practice to add $properties = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
87 100
            $this->exists
88 100
                ? $this->fresh() ?? $this
89 100
                : $this
90
        );
91
92 100
        if (static::eventsToBeRecorded()->contains('updated') && $processingEvent == 'updated') {
93 52
            $nullProperties = array_fill_keys(array_keys($properties['attributes']), null);
94
95 52
            $properties['old'] = array_merge($nullProperties, $this->oldAttributes);
96
97 52
            $this->oldAttributes = [];
98
        }
99
100 100
        if ($this->shouldLogOnlyDirty() && isset($properties['old'])) {
101 24
            $properties['attributes'] = array_udiff_assoc(
102 24
                $properties['attributes'],
103 24
                $properties['old'],
104
                function ($new, $old) {
105 24
                    return $new <=> $old;
106 24
                }
107
            );
108 24
            $properties['old'] = collect($properties['old'])
109 24
                ->only(array_keys($properties['attributes']))
110 24
                ->all();
111
        }
112
113 100
        return $properties;
114
    }
115
116 124
    public static function logChanges(Model $model): array
117
    {
118 124
        $changes = [];
119 124
        $attributes = $model->attributesToBeLogged();
120
121 124
        foreach ($attributes as $attribute) {
122 100
            if (Str::contains($attribute, '.')) {
123 20
                $changes += self::getRelatedModelAttributeValue($model, $attribute);
124 100
            } else {
125 24
                $changes[$attribute] = $model->getAttribute($attribute);
126 24
127
                if (
128
                    in_array($attribute, $model->getDates())
129 100
                    && ! is_null($changes[$attribute])
130
                ) {
131
                    $changes[$attribute] = $model->serializeDate(
132
                        $model->asDateTime($changes[$attribute])
133 124
                    );
134
                }
135
            }
136 20
        }
137
138 20
        return $changes;
139
    }
140
141
    protected static function getRelatedModelAttributeValue(Model $model, string $attribute): array
142 20
    {
143
        if (substr_count($attribute, '.') > 1) {
144 20
            throw CouldNotLogChanges::invalidAttribute($attribute);
145
        }
146 20
147
        [$relatedModelName, $relatedAttribute] = explode('.', $attribute);
148
149
        $relatedModel = $model->$relatedModelName ?? $model->$relatedModelName();
150
151
        return ["{$relatedModelName}.{$relatedAttribute}" => $relatedModel->$relatedAttribute ?? null];
152
    }
153
}
154