DBUuid::requireField()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 4
c 3
b 1
f 0
dl 0
loc 10
rs 10
cc 2
nc 2
nop 0
1
<?php
2
3
namespace LeKoala\Uuid;
4
5
use Ramsey\Uuid\Uuid;
6
use SilverStripe\ORM\DB;
7
use Tuupola\Base62Proxy;
8
use SilverStripe\Core\Convert;
9
use SilverStripe\ORM\DataObject;
10
use SilverStripe\Forms\FormField;
11
use SilverStripe\Model\ModelData;
12
use SilverStripe\ORM\FieldType\DBField;
13
14
/**
15
 * A uuid field that stores Uuid in binary formats
16
 *
17
 * Some knowledge...
18
 *
19
 * @link https://paragonie.com/blog/2015/09/comprehensive-guide-url-parameter-encryption-in-php
20
 * @link https://www.percona.com/blog/2014/12/19/store-uuid-optimized-way/
21
 * @link https://mariadb.com/kb/en/library/guiduuid-performance/
22
 * @link https://stackoverflow.com/questions/28251144/inserting-and-selecting-uuids-as-binary16
23
 */
24
class DBUuid extends DBField
25
{
26
    protected const BINARY_LENGTH = 16;
27
28
    protected const STRING_LENGTH = 36;
29
30
    /**
31
     * An expression to use in your custom queries
32
     *
33
     * @link https://stackoverflow.com/questions/37168797/how-to-format-uuid-string-from-binary-column-in-mysql-mariadb
34
     * @return string
35
     */
36
    public static function sqlFormatExpr()
37
    {
38
        $sql = <<<SQL
39
LOWER(CONCAT(
40
SUBSTR(HEX(Uuid), 1, 8), '-',
41
SUBSTR(HEX(Uuid), 9, 4), '-',
42
SUBSTR(HEX(Uuid), 13, 4), '-',
43
SUBSTR(HEX(Uuid), 17, 4), '-',
44
SUBSTR(HEX(Uuid), 21)
45
)) AS UuidFormatted
46
SQL;
47
        return $sql;
48
    }
49
50
    /**
51
     * This can be used with ->where clause
52
     *
53
     * @param string $type like, =
54
     * @param string $value
55
     * @return string
56
     */
57
    public function filterExpression($type, $value)
58
    {
59
        if ($type == "like") {
60
            $value = "%$value%";
61
        }
62
        $value = str_replace('-', '', $value);
63
        /** @var string $value */
64
        $value = Convert::raw2sql($value, true);
65
        return "LOWER(HEX({$this->name})) $type $value";
66
    }
67
68
    /**
69
     * @return void
70
     */
71
    public function requireField(): void
72
    {
73
        // Use direct sql statement here
74
        $sql = "binary(16)";
75
        // In postgres, it's bytea, there is also an uuid but we would need some postgres specific logic
76
        // @link https://stackoverflow.com/questions/26990559/convert-mysql-binary-to-postgresql-bytea
77
        if (self::isPostgreSQL()) {
78
            $sql = 'bytea';
79
        }
80
        DB::require_field($this->tableName, $this->name, $sql);
81
    }
82
83
    protected static function isPostgreSQL(): bool
84
    {
85
        $conn = DB::get_conn();
86
        if (!$conn) {
87
            return false;
88
        }
89
        $class = strtolower(get_class($conn));
90
        return str_contains($class, 'postgres');
91
    }
92
93
    /**
94
     * @return ?string A uuid identifier like 0564a64ecdd4a2-7731-3233-3435-7cea2b
95
     */
96
    public function Nice()
97
    {
98
        if (!$this->value) {
99
            return $this->nullValue();
100
        }
101
        return Uuid::fromBytes($this->value)->toString();
102
    }
103
104
    /**
105
     * Return raw value since we store binary(16) representation
106
     *
107
     * @return ?string The binary representation like b"\x05d¦NÍÔ¢w12345|ê+
108
     */
109
    public function Bytes()
110
    {
111
        if (!$this->value) {
112
            return $this->nullValue();
113
        }
114
        return $this->value;
115
    }
116
117
    /**
118
     * Perfect for urls or html usage
119
     *
120
     * @return ?string A base62 representation like 6a630O1jrtMjCrQDyG3D3O
121
     */
122
    public function Base62()
123
    {
124
        if (!$this->value) {
125
            return $this->nullValue();
126
        }
127
        return Base62Proxy::encode($this->value);
128
    }
129
130
    /**
131
     * @param string $title
132
     * @param array<mixed> $params
133
     * @return FormField|null
134
     */
135
    public function scaffoldFormField(?string $title = null, array $params = []): ?FormField
136
    {
137
        return null;
138
    }
139
140
    /**
141
     * @return null
142
     */
143
    public function nullValue(): mixed
144
    {
145
        return null;
146
    }
147
148
    /**
149
     * @param mixed $value
150
     * @param DataObject|array<string,mixed> $record
151
     * @param boolean $markChanged
152
     */
153
    public function setValue(mixed $value, null|array|ModelData $record = null, bool $markChanged = true): static
154
    {
155
        if ($value && is_string($value) && strlen($value) > self::BINARY_LENGTH && Uuid::isValid($value)) {
156
            $value = Uuid::fromString($value)->getBytes();
157
        }
158
        $this->value = $value;
159
        return $this;
160
    }
161
162
    /**
163
     * @param mixed $value
164
     * @return mixed
165
     */
166
    public function prepValueForDB(mixed $value): mixed
167
    {
168
        if (!$value) {
169
            return $this->nullValue();
170
        }
171
        // Uuid in string format have 36 chars
172
        // Strlen 16 = already binary
173
        if (strlen($value) === self::BINARY_LENGTH) {
174
            return $value;
175
        }
176
        return Uuid::fromString($value)->getBytes();
177
    }
178
}
179