Completed
Push — master ( 96876e...1a4b44 )
by Carlos
03:22
created

Versionable   A

Complexity

Total Complexity 35

Size/Duplication

Total Lines 243
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 1

Test Coverage

Coverage 73.75%

Importance

Changes 0
Metric Value
wmc 35
lcom 3
cbo 1
dl 0
loc 243
ccs 59
cts 80
cp 0.7375
rs 9.6
c 0
b 0
f 0

20 Methods

Rating   Name   Duplication   Size   Complexity  
A bootVersionable() 0 14 2
A createVersionForModel() 0 7 2
A versions() 0 4 1
A lastVersion() 0 4 1
A getVersion() 0 4 1
A revertToVersion() 0 4 1
A removeOldVersions() 0 8 2
A removeAllVersions() 0 4 1
A shouldVersioning() 0 4 2
A getVersionableAttributes() 0 21 4
A setVersionable() 0 10 2
A setDontVersionable() 0 10 2
A getVersionable() 0 4 2
A getDontVersionable() 0 4 2
A getVersionStrategy() 0 4 2
A setVersionStrategy() 0 10 2
A getVersionModel() 0 4 1
A getKeepVersionsCount() 0 4 1
A versionableFromArray() 0 12 3
A withoutVersion() 0 8 1
1
<?php
2
3
/*
4
 * This file is part of the overtrue/laravel-versionable.
5
 *
6
 * (c) overtrue <[email protected]>
7
 *
8
 * This source file is subject to the MIT license that is bundled.
9
 */
10
11
namespace Overtrue\LaravelVersionable;
12
13
use Illuminate\Database\Eloquent\Model;
14
use Illuminate\Database\Eloquent\Relations\MorphMany;
15
use Illuminate\Database\Eloquent\Relations\MorphOne;
16
17
trait Versionable
18
{
19
    static protected $versioning = true;
20
21
    // You can add these properties to you versionable model
22
    //protected $versionable = [];
23
    //protected $dontVersionable = ['*'];
24
25 6
    public static function bootVersionable()
26
    {
27
        static::saved(function (Model $model) {
28 6
            self::createVersionForModel($model);
29 6
        });
30
31
        static::deleted(function (Model $model) {
32
            if ($model->forceDeleting) {
33
                $model->removeAllVersions();
34
            } else {
35
                self::createVersionForModel($model);
36
            }
37 6
        });
38 6
    }
39
40 6
    private static function createVersionForModel(Model $model): void
41
    {
42 6
        if ($model->shouldVersioning()) {
43 6
            Version::createForModel($model);
44 6
            $model->removeOldVersions($model->getKeepVersionsCount());
45
        }
46 6
    }
47
48
    /**
49
     * @return \Illuminate\Database\Eloquent\Relations\MorphMany
50
     */
51 6
    public function versions(): MorphMany
52
    {
53 6
        return $this->morphMany(\config('versionable.version_model'), 'versionable')->latest('id');
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?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
54
    }
55
56
    /**
57
     * @return \Illuminate\Database\Eloquent\Relations\MorphOne
58
     */
59 4
    public function lastVersion(): MorphOne
60
    {
61 4
        return $this->morphOne(\config('versionable.version_model'), 'versionable')->latest('id');
0 ignored issues
show
Bug introduced by
It seems like morphOne() must be provided by classes using this trait. How about adding it as abstract method to this trait?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
62
    }
63
64
    /**
65
     * @param int $id
66
     *
67
     * @return \Illuminate\Database\Eloquent\Model|null
68
     */
69 1
    public function getVersion($id)
70
    {
71 1
        return $this->versions()->find($id);
72
    }
73
74
    /**
75
     * @param int $id
76
     *
77
     * @return mixed
78
     */
79 1
    public function revertToVersion($id)
80
    {
81 1
        return $this->versions()->findOrFail($id)->revert();
82
    }
83
84
    /**
85
     * @param int $keep
86
     */
87 6
    public function removeOldVersions(int $keep): void
88
    {
89 6
        if ($keep <= 0) {
90 5
            return;
91
        }
92
93 1
        $this->versions()->skip($keep)->take($keep)->get()->each->delete();
94 1
    }
95
96 1
    public function removeAllVersions()
97
    {
98 1
        $this->versions->each->delete();
0 ignored issues
show
Bug introduced by
The property versions does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
99 1
    }
100
101
    /**
102
     * @return bool
103
     */
104 6
    public function shouldVersioning(): bool
105
    {
106 6
        return self::$versioning && !empty($this->getVersionableAttributes());
107
    }
108
109
    /**
110
     * @return array
111
     */
112 6
    public function getVersionableAttributes(): array
113
    {
114 6
        $changes = $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?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
115
116 6
        if (empty($changes)) {
117
            return [];
118
        }
119
120 6
        switch ($this->getVersionStrategy()) {
121 6
            case VersionStrategy::DIFF:
122 6
                $contents = $changes;
123 6
                break;
124 1
            case VersionStrategy::SNAPSHOT:
125 1
                $contents = $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?

This check looks for methods that are used by a trait but not required by it.

To illustrate, let’s look at the following code example

trait Idable {
    public function equalIds(Idable $other) {
        return $this->getId() === $other->getId();
    }
}

The trait Idable provides a method equalsId that in turn relies on the method getId(). If this method does not exist on a class mixing in this trait, the method will fail.

Adding the getId() as an abstract method to the trait will make sure it is available.

Loading history...
126 1
                break;
127
            default:
128
                $contents = $changes;
129
        }
130
131 6
        return $this->versionableFromArray($contents);
132
    }
133
134
    /**
135
     * @param array $attributes
136
     *
137
     * @return $this
138
     *
139
     * @throws \Exception
140
     */
141
    public function setVersionable(array $attributes)
142
    {
143
        if (!\property_exists($this, 'versionable')) {
144
            throw new \Exception('Property $versionable not exist.');
145
        }
146
147
        $this->versionable = $attributes;
0 ignored issues
show
Bug introduced by
The property versionable does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
148
149
        return $this;
150
    }
151
152
    /**
153
     * @param array $attributes
154
     *
155
     * @return $this
156
     *
157
     * @throws \Exception
158
     */
159
    public function setDontVersionable(array $attributes)
160
    {
161
        if (!\property_exists($this, 'dontVersionable')) {
162
            throw new \Exception('Property $dontVersionable not exist.');
163
        }
164
165
        $this->dontVersionable = $attributes;
0 ignored issues
show
Bug introduced by
The property dontVersionable does not seem to exist. Did you mean versionable?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
166
167
        return $this;
168
    }
169
170
    /**
171
     * @return array
172
     */
173 6
    public function getVersionable(): array
174
    {
175 6
        return \property_exists($this, 'versionable') ? $this->versionable : [];
176
    }
177
178
    /**
179
     * @return array
180
     */
181
    public function getDontVersionable(): array
182
    {
183
        return \property_exists($this, 'dontVersionable') ? $this->dontVersionable : [];
0 ignored issues
show
Bug introduced by
The property dontVersionable does not seem to exist. Did you mean versionable?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
184
    }
185
186
    /**
187
     * @return string
188
     */
189 6
    public function getVersionStrategy()
190
    {
191 6
        return \property_exists($this, 'versionStrategy') ? $this->versionStrategy : VersionStrategy::DIFF;
0 ignored issues
show
Bug introduced by
The property versionStrategy does not seem to exist. Did you mean versions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
192
    }
193
194
    /**
195
     * @param string $strategy
196
     *
197
     * @return $this
198
     *
199
     * @throws \Exception
200
     */
201 1
    public function setVersionStrategy(string $strategy)
202
    {
203 1
        if (!\property_exists($this, 'versionStrategy')) {
204
            throw new \Exception('Property $versionStrategy not exist.');
205
        }
206
207 1
        $this->versionStrategy = $strategy;
0 ignored issues
show
Bug introduced by
The property versionStrategy does not seem to exist. Did you mean versions?

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
208
209 1
        return $this;
210
    }
211
212
    /**
213
     * @return string
214
     */
215 6
    public function getVersionModel(): string
216
    {
217 6
        return config('versionable.version_model');
218
    }
219
220
    /**
221
     * @return string
222
     */
223 6
    public function getKeepVersionsCount(): string
224
    {
225 6
        return config('versionable.keep_versions', 0);
226
    }
227
228
    /**
229
     * Get the versionable attributes of a given array.
230
     *
231
     * @param array $attributes
232
     *
233
     * @return array
234
     */
235 6
    public function versionableFromArray(array $attributes): array
236
    {
237 6
        if (count($this->getVersionable()) > 0) {
238 6
            return \array_intersect_key($attributes, array_flip($this->getVersionable()));
239
        }
240
241
        if (count($this->getDontVersionable()) > 0) {
242
            return \array_diff_key($attributes, array_flip($this->getDontVersionable()));
243
        }
244
245
        return $attributes;
246
    }
247
248
    /**
249
     * @param callable $callback
250
     */
251 1
    public static function withoutVersion(callable $callback)
252
    {
253 1
        self::$versioning = false;
254
255 1
        \call_user_func($callback);
256
257 1
        self::$versioning = true;
258 1
    }
259
}
260