Passed
Push — master ( 79b20e...b11f1b )
by Michael
02:19 queued 10s
created

GeneratesUuid::castAttribute()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 3.1406

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 3
eloc 3
c 2
b 0
f 0
nc 2
nop 2
dl 0
loc 7
ccs 3
cts 4
cp 0.75
crap 3.1406
rs 10
1
<?php
2
3
namespace Dyrynda\Database\Support;
4
5
use Ramsey\Uuid\Uuid;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Str;
8
use Illuminate\Database\Eloquent\Builder;
9
use Illuminate\Contracts\Support\Arrayable;
10
11
/**
12
 * UUID generation trait.
13
 *
14
 * Include this trait in any Eloquent model where you wish to automatically set
15
 * a UUID field. When saving, if the UUID field has not been set, generate a
16
 * new UUID value, which will be set on the model and saved by Eloquent.
17
 *
18
 * @copyright 2017 Michael Dyrynda
19
 * @author    Michael Dyrynda <[email protected]>
20
 * @license   MIT
21
 *
22
 * @property  string  $uuidVersion
23
 */
24
trait GeneratesUuid
25
{
26
    /**
27
     * The UUID versions.
28
     *
29
     * @var array
30
     */
31
    protected $uuidVersions = [
32
        'uuid1',
33
        'uuid3',
34
        'uuid4',
35
        'uuid5',
36
        'ordered',
37
    ];
38
39
    /**
40
     * Determine whether an attribute should be cast to a native type.
41
     *
42
     * @param  string  $key
43
     * @param  array|string|null  $types
44
     * @return bool
45
     */
46
    abstract public function hasCast($key, $types = null);
47
48
    /**
49
     * Boot the trait, adding a creating observer.
50
     *
51
     * When persisting a new model instance, we resolve the UUID field, then set
52
     * a fresh UUID, taking into account if we need to cast to binary or not.
53
     *
54
     * @return void
55
     */
56 12
    public static function bootGeneratesUuid(): void
57
    {
58
        static::creating(function ($model) {
59 12
            foreach ($model->uuidColumns() as $item) {
60
                /* @var \Illuminate\Database\Eloquent\Model|static $model */
61 12
                $uuid = $model->resolveUuid();
62
63 12
                if (isset($model->attributes[$item]) && ! is_null($model->attributes[$item])) {
64
                    /* @var \Ramsey\Uuid\Uuid $uuid */
65 7
                    $uuid = $uuid->fromString(strtolower($model->attributes[$item]));
66
                }
67
68 12
                $model->{$item} = strtolower($uuid->toString());
69
            }
70 12
        });
71 6
    }
72
73
    /**
74
     * The name of the column that should be used for the UUID.
75
     *
76
     * @return string
77
     */
78 8
    public function uuidColumn(): string
79
    {
80 8
        return 'uuid';
81
    }
82
83
    /**
84
     * The names of the columns that should be used for the UUID.
85
     *
86
     * @return array
87
     */
88 11
    public function uuidColumns(): array
89
    {
90 11
        return [$this->uuidColumn()];
91
    }
92
93
    /**
94
     * Resolve a UUID instance for the configured version.
95
     *
96
     * @return \Ramsey\Uuid\Uuid
97
     */
98 12
    public function resolveUuid(): Uuid
99
    {
100 12
        if (($version = $this->resolveUuidVersion()) == 'ordered') {
101 1
            return Str::orderedUuid();
0 ignored issues
show
Bug Best Practice introduced by
The expression return Illuminate\Support\Str::orderedUuid() returns the type Ramsey\Uuid\UuidInterface which includes types incompatible with the type-hinted return Ramsey\Uuid\Uuid.
Loading history...
102
        }
103
104 11
        return call_user_func([Uuid::class, $version]);
105
    }
106
107
    /**
108
     * Resolve the UUID version to use when setting the UUID value. Default to uuid4.
109
     *
110
     * @return string
111
     */
112 18
    public function resolveUuidVersion(): string
113
    {
114 18
        if (property_exists($this, 'uuidVersion') && in_array($this->uuidVersion, $this->uuidVersions)) {
115 6
            return $this->uuidVersion;
116
        }
117
118 12
        return 'uuid4';
119
    }
120
121
    /**
122
     * Scope queries to find by UUID.
123
     *
124
     * @param  \Illuminate\Database\Eloquent\Builder  $query
125
     * @param  string  $uuid
126
     * @param  string  $uuidColumn
127
     *
128
     * @return \Illuminate\Database\Eloquent\Builder
129
     */
130 5
    public function scopeWhereUuid($query, $uuid, $uuidColumn = null): Builder
131
    {
132 5
        $uuidColumn = ! is_null($uuidColumn) && in_array($uuidColumn, $this->uuidColumns())
133 2
            ? $uuidColumn
134 5
            : $this->uuidColumns()[0];
135
136
        return $query->whereIn($uuidColumn, array_map(function ($uuid) {
0 ignored issues
show
Bug Best Practice introduced by
The expression return $query->whereIn($...port\Arr::wrap($uuid))) could return the type Illuminate\Database\Query\Builder which is incompatible with the type-hinted return Illuminate\Database\Eloquent\Builder. Consider adding an additional type-check to rule them out.
Loading history...
137 5
            return strtolower($uuid);
138 5
        }, Arr::wrap($uuid)));
139
    }
140
141
    /**
142
     * Convert a single UUID or array of UUIDs to bytes.
143
     *
144
     * @param  \Illuminate\Contracts\Support\Arrayable|array|string  $uuid
145
     * @return array
146
     */
147
    protected function bytesFromUuid($uuid): array
148
    {
149
        if (is_array($uuid) || $uuid instanceof Arrayable) {
150
            array_walk($uuid, function (&$uuid) {
0 ignored issues
show
Bug introduced by
It seems like $uuid can also be of type Illuminate\Contracts\Support\Arrayable; however, parameter $array of array_walk() 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 ignore-type  annotation

150
            array_walk(/** @scrutinizer ignore-type */ $uuid, function (&$uuid) {
Loading history...
151
                $uuid = $this->resolveUuid()->fromString($uuid)->getBytes();
152
            });
153
154
            return $uuid;
155
        }
156
157
        return Arr::wrap($this->resolveUuid()->fromString($uuid)->getBytes());
158
    }
159
}
160