VersionableTrait::enableVersioning()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 2
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 5
rs 10
1
<?php
2
3
namespace Yaro\Jarboe\Models\Traits;
4
5
use Illuminate\Database\Eloquent\Relations\MorphMany;
6
use Yaro\Jarboe\Models\Version;
7
8
trait VersionableTrait
9
{
10
    /**
11
     * Retrieve, if exists, the property that define that Version model.
12
     * If no property defined, use the default Version model.
13
     *
14
     * Trait cannot share properties whth their class !
15
     * http://php.net/manual/en/language.oop5.traits.php
16
     * @return unknown|string
0 ignored issues
show
Bug introduced by
The type Yaro\Jarboe\Models\Traits\unknown 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...
17
     */
18
    protected function getVersionClass()
19
    {
20
        if (property_exists(self::class, 'versionClass')) {
21
            return $this->versionClass;
22
        }
23
24
        return config('jarboe.versionable.version_model', Version::class);
25
    }
26
27
    /**
28
     * Private variable to detect if this is an update
29
     * or an insert
30
     * @var bool
31
     */
32
    private $updating;
33
34
    /**
35
     * Contains all dirty data that is valid for versioning
36
     *
37
     * @var array
38
     */
39
    private $versionableDirtyData;
40
41
    /**
42
     * Optional reason, why this version was created
43
     * @var string
44
     */
45
    private $reason;
46
47
    /**
48
     * Flag that determines if the model allows versioning at all
49
     * @var bool
50
     */
51
    protected $versioningEnabled = true;
52
53
    /**
54
     * @return $this
55
     */
56
    public function enableVersioning()
57
    {
58
        $this->versioningEnabled = true;
59
60
        return $this;
61
    }
62
63
    /**
64
     * @return $this
65
     */
66
    public function disableVersioning()
67
    {
68
        $this->versioningEnabled = false;
69
70
        return $this;
71
    }
72
73
    /**
74
     * Attribute mutator for "reason"
75
     * Prevent "reason" to become a database attribute of model
76
     *
77
     * @param string $value
78
     */
79
    public function setReasonAttribute($value)
80
    {
81
        $this->reason = $value;
82
    }
83
84
    /**
85
     * Initialize model events
86
     */
87
    public static function bootVersionableTrait()
88
    {
89
        static::saving(function ($model) {
90
            $model->versionablePreSave();
91
        });
92
93
        static::saved(function ($model) {
94
            $model->versionablePostSave();
95
        });
96
    }
97
98
    /**
99
     * Return all versions of the model
100
     * @return MorphMany
101
     */
102
    public function versions()
103
    {
104
        return $this->morphMany($this->getVersionClass(), 'versionable');
0 ignored issues
show
Bug introduced by
It seems like morphMany() 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

104
        return $this->/** @scrutinizer ignore-call */ morphMany($this->getVersionClass(), 'versionable');
Loading history...
105
    }
106
107
    /**
108
     * Returns the latest version available
109
     * @return Version
110
     */
111
    public function currentVersion()
112
    {
113
        $class = $this->getVersionClass();
114
        return $this->versions()->orderBy($class::CREATED_AT, 'DESC')->first();
115
    }
116
117
    /**
118
     * Returns the previous version
119
     * @return Version
120
     */
121
    public function previousVersion()
122
    {
123
        return $this->versions()->latest()->limit(1)->offset(1)->first();
124
    }
125
126
    /**
127
     * Get a model based on the version id
128
     *
129
     * @param $version_id
130
     *
131
     * @return $this|null
132
     */
133
    public function getVersionModel($version_id)
134
    {
135
        $version = $this->versions()->where('version_id', '=', $version_id)->first();
136
        if (!is_null($version)) {
137
            return $version->getModel();
138
        }
139
140
        return null;
141
    }
142
143
    /**
144
     * Pre save hook to determine if versioning is enabled and if we're updating
145
     * the model
146
     * @return void
147
     */
148
    protected function versionablePreSave()
149
    {
150
        if ($this->versioningEnabled === true) {
151
            $this->versionableDirtyData = $this->getDirty();
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

151
            /** @scrutinizer ignore-call */ 
152
            $this->versionableDirtyData = $this->getDirty();
Loading history...
152
            $this->updating = $this->exists;
153
        }
154
    }
155
156
    /**
157
     * Save a new version.
158
     * @return void
159
     */
160
    protected function versionablePostSave()
161
    {
162
        /**
163
         * We'll save new versions on updating and first creation
164
         */
165
        if (
166
            ($this->versioningEnabled === true && $this->updating && $this->isValidForVersioning())
167
            || ($this->versioningEnabled === true && !$this->updating && !is_null($this->versionableDirtyData) && count($this->versionableDirtyData))
168
        ) {
169
            // Save a new version
170
            $class = $this->getVersionClass();
171
            $version = new $class();
172
            $version->versionable_id = $this->getKey();
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

172
            /** @scrutinizer ignore-call */ 
173
            $version->versionable_id = $this->getKey();
Loading history...
173
            $version->versionable_type = get_class($this);
174
            $version->user_id = $this->getAuthUserId();
175
            $version->auth_guard = $this->getAuthGuard();
176
            // https://github.com/mpociot/versionable/issues/43
177
            $version->model_data = serialize($this->attributesToArray());
0 ignored issues
show
Bug introduced by
It seems like attributesToArray() 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

177
            $version->model_data = serialize($this->/** @scrutinizer ignore-call */ attributesToArray());
Loading history...
178
179
            if (!empty($this->reason)) {
180
                $version->reason = $this->reason;
181
            }
182
183
            $version->save();
184
185
            $this->purgeOldVersions();
186
        }
187
    }
188
189
    /**
190
     * Delete old versions of this model when the reach a specific count.
191
     *
192
     * @return void
193
     */
194
    private function purgeOldVersions()
195
    {
196
        $keep = isset($this->keepOldVersions) ? $this->keepOldVersions : 0;
197
        $count = $this->versions()->count();
198
199
        if ((int)$keep > 0 && $count > $keep) {
200
            $this->versions()
201
                ->latest()
202
                ->take($count)
203
                ->skip($keep)
204
                ->get()
205
                ->each(function ($version) {
206
                    $version->delete();
207
                });
208
        }
209
    }
210
211
    /**
212
     * Determine if a new version should be created for this model.
213
     *
214
     * @return bool
215
     */
216
    private function isValidForVersioning()
217
    {
218
        $dontVersionFields = isset($this->dontVersionFields) ? $this->dontVersionFields : [];
219
        $removeableKeys = array_merge($dontVersionFields, [$this->getUpdatedAtColumn()]);
0 ignored issues
show
Bug introduced by
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 ignore-call  annotation

219
        $removeableKeys = array_merge($dontVersionFields, [$this->/** @scrutinizer ignore-call */ getUpdatedAtColumn()]);
Loading history...
220
221
        if (method_exists($this, 'getDeletedAtColumn')) {
222
            $removeableKeys[] = $this->getDeletedAtColumn();
223
        }
224
225
        return (count(array_diff_key($this->versionableDirtyData, array_flip($removeableKeys))) > 0);
226
    }
227
228
    /**
229
     * @return int|null
230
     */
231
    protected function getAuthUserId()
232
    {
233
        $auth = auth();
234
        if ($auth->check()) {
235
            return $auth->id();
236
        }
237
238
        return null;
239
    }
240
241
    /**
242
     * @return string|null
243
     */
244
    protected function getAuthGuard()
245
    {
246
        $auth = auth();
247
        if ($auth->check()) {
248
            return $auth->getDefaultDriver();
249
        }
250
251
        return null;
252
    }
253
}
254