Test Failed
Push — develop ( 864385...6b671f )
by Freddie
03:26
created

SchemaAttribute::isPk()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of FlexPHP.
4
 *
5
 * (c) Freddie Gar <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace FlexPHP\Schema;
11
12
use Exception;
13
use FlexPHP\Schema\Constants\Keyword;
14
use FlexPHP\Schema\Constants\Rule;
15
use FlexPHP\Schema\Exception\InvalidSchemaAttributeException;
16
use FlexPHP\Schema\Validations\SchemaAttributeLogicValidation;
17
use FlexPHP\Schema\Validations\SchemaAttributeValidation;
18
19
final class SchemaAttribute implements SchemaAttributeInterface
20
{
21
    /**
22
     * @var string
23
     */
24
    private $name;
25
26
    /**
27
     * @var string
28
     */
29
    private $dataType;
30
31
    /**
32
     * @var array<string, mixed>
33
     */
34
    private $constraints = [];
35
36
    /**
37 118
     * @param mixed $constraints
38
     */
39 118
    public function __construct(string $name, string $dataType, $constraints = null)
40 118
    {
41 118
        $this->setName($name);
42
        $this->setDataType($dataType);
43 118
        $this->setConstraints($constraints);
44 85
45
        $this->validate();
46 118
    }
47
48 118
    public function name(): string
49
    {
50
        return $this->name;
51 118
    }
52
53 118
    public function dataType(): string
54
    {
55
        return $this->dataType;
56 118
    }
57
58 118
    public function constraints(): array
59
    {
60
        return $this->constraints;
61 6
    }
62
63 6
    public function type(): ?string
64
    {
65
        return $this->constraints[Rule::TYPE] ?? null;
66 31
    }
67
68 31
    public function isRequired(): bool
69
    {
70
        return (bool)($this->constraints[Rule::REQUIRED] ?? false);
71 66
    }
72
73 66
    public function minLength(): ?int
74
    {
75
        return $this->constraints[Rule::MINLENGTH] ?? null;
76 61
    }
77
78 61
    public function maxLength(): ?int
79
    {
80
        return $this->constraints[Rule::MAXLENGTH] ?? null;
81 32
    }
82
83 32
    public function minCheck(): ?int
84
    {
85
        return $this->constraints[Rule::MINCHECK] ?? null;
86 31
    }
87
88 31
    public function maxCheck(): ?int
89
    {
90
        return $this->constraints[Rule::MAXCHECK] ?? null;
91 62
    }
92
93 62
    public function min(): ?int
94
    {
95
        return $this->constraints[Rule::MIN] ?? null;
96 59
    }
97
98 59
    public function max(): ?int
99
    {
100
        return $this->constraints[Rule::MAX] ?? null;
101 6
    }
102
103 6
    public function equalTo(): ?string
104
    {
105
        return $this->constraints[Rule::EQUALTO] ?? null;
106 99
    }
107
108 99
    public function isPk(): bool
109
    {
110
        return (bool)($this->constraints[Rule::PRIMARYKEY] ?? false);
111 94
    }
112
113 94
    public function isAi(): bool
114
    {
115
        return (bool)($this->constraints[Rule::AUTOINCREMENT] ?? false);
116 24
    }
117
118 24
    public function isFk(): bool
119
    {
120
        return (bool)($this->constraints[Rule::FOREIGNKEY] ?? false);
121 9
    }
122
123 9
    public function fkTable(): ?string
124
    {
125
        return $this->constraints[Rule::FOREIGNKEY]['table'] ?? null;
126 9
    }
127
128 9
    public function fkId(): ?string
129
    {
130
        return $this->constraints[Rule::FOREIGNKEY]['id'] ?? null;
131 9
    }
132
133 9
    public function fkName(): ?string
134
    {
135
        return $this->constraints[Rule::FOREIGNKEY]['name'] ?? null;
136 91
    }
137
138 91
    public function isCa(): bool
139
    {
140
        return (bool)($this->constraints[Rule::CREATEDAT] ?? false);
141 90
    }
142
143 90
    public function isUa(): bool
144
    {
145
        return (bool)($this->constraints[Rule::UPDATEDAT] ?? false);
146 91
    }
147
148 91
    public function isBlame(): bool
149
    {
150
        return $this->isCa() || $this->isUa();
151 118
    }
152
153
    public function properties(): array
154 118
    {
155 118
        return [
156 118
            Keyword::NAME => $this->name(),
157
            Keyword::DATATYPE => $this->dataType(),
158
            Keyword::CONSTRAINTS => $this->constraints(),
159
        ];
160 79
    }
161
162
    public function typeHint(): string
163 79
    {
164
        $typeHintByDataType = [
165
            'smallint' => 'int',
166
            'integer' => 'int',
167
            'float' => 'float',
168
            'double' => 'float',
169
            'bool' => 'bool',
170
            'boolean' => 'bool',
171
            'date' => '\DateTime',
172
            'date_immutable' => '\DateTimeImmutable',
173
            'datetime' => '\DateTime',
174
            'datetime_immutable' => '\DateTimeImmutable',
175
            'datetimetz' => '\DateTime',
176
            'datetimetz_immutable' => '\DateTimeImmutable',
177
            'time' => '\DateTime',
178
            'time_immutable' => '\DateTimeImmutable',
179
            'array' => 'array',
180
            'simple_array' => 'array',
181
            'json_array' => 'array',
182 79
        ];
183 48
184
        if (isset($typeHintByDataType[$this->dataType()])) {
185
            return $typeHintByDataType[$this->dataType()];
186 36
        }
187
188
        return 'string';
189 118
    }
190
191 118
    private function validate(): void
192 115
    {
193 85
        try {
194
            (new SchemaAttributeValidation($this->properties()))->validate();
195 118
            (new SchemaAttributeLogicValidation($this))->validate();
196
        } catch (Exception $e) {
197 118
            throw new InvalidSchemaAttributeException(
198 118
                \sprintf(
199
                    'Attribute %s > %s',
200 118
                    $this->name(),
201
                    $e->getMessage()
202 118
                )
203 118
            );
204
        }
205
    }
206
207
    private function setName(string $name): void
208 118
    {
209
        $this->name = $name;
210 118
    }
211 98
212 63
    private function setDataType(string $dataType): void
213
    {
214 35
        $this->dataType = $dataType;
215
    }
216
217 118
    /**
218
     * @param mixed $constraints
219 63
     */
220
    private function setConstraints($constraints): void
221 63
    {
222 63
        if (!empty($constraints)) {
223
            if (\is_string($constraints)) {
224 98
                $this->setConstraintsFromString($constraints);
225
            } else {
226 98
                $this->setConstraintsFromArray($constraints);
227 98
            }
228
        }
229 63
    }
230
231 63
    private function setConstraintsFromString(string $constraints): void
232
    {
233
        $this->setConstraintsFromArray($this->getConstraintsFromString($constraints));
234 63
    }
235 63
236
    private function setConstraintsFromArray(array $constraints): void
237 63
    {
238 51
        $this->constraints = $this->getConstraintsCast($constraints);
239
    }
240 51
241 2
    private function getConstraintsFromString(string $constraints): array
242 2
    {
243 49
        $_constraints = \explode('|', $constraints);
244 10
245 43
        /** @var mixed $_constraint */
246 10
        foreach ($_constraints as $index => $_constraint) {
247
            $_rule = \explode(':', $_constraint);
248
249 51
            if (\count($_rule) === 2) {
250
                [$_name, $_options] = $_rule;
251 24
252
                if (Rule::FOREIGNKEY !== $_name && \strpos($_options, ',') !== false) { // Range
253
                    [$min, $max] = \explode(',', $_options);
254 63
                    $_options = \compact('min', 'max');
255
                } elseif (\preg_match('/^false$/i', $_options)) { // False as string
256
                    $_options = false;
257 63
                } elseif (\preg_match('/^true$/i', $_options)) { // True as string
258
                    $_options = true;
259
                }
260 98
261
                $_constraints[$_name] = $_options;
262 98
            } else {
263 98
                $_constraints[$_rule[0]] = true;
264 10
            }
265 10
266 93
            unset($_constraints[$index]);
267 4
        }
268 4
269 4
        return $_constraints;
270 89
    }
271 10
272
    private function getConstraintsCast(array $constraints): array
273 98
    {
274
        foreach ($constraints as $name => $value) {
275
            if (\is_int($name)) {
276
                $constraints[$value] = true;
277 98
                unset($constraints[$name]);
278
            } elseif ($name === Rule::CHECK || $name === Rule::LENGTH) {
279
                $constraints['min' . $name] = (int)$value['min'];
280 10
                $constraints['max' . $name] = (int)$value['max'];
281
                unset($constraints[$name]);
282 10
            } elseif ($name === Rule::FOREIGNKEY && \is_string($value)) {
283 10
                $constraints[$name] = $this->getFkOptions($value);
284 10
            } else {
285
                $constraints[$name] = \is_numeric($value) ? (int)$value : $value;
286 10
            }
287 10
        }
288 2
289
        return $constraints;
290 2
    }
291 8
292 4
    private function getFkOptions(string $constraint): array
293
    {
294 4
        $_vars = \explode(',', $constraint);
295
        $fkName = 'name';
296 4
        $fkId = 'id';
297
298 4
        switch (\count($_vars)) {
299
            case 3:
300
                [$fkTable, $fkName, $fkId] = $_vars;
301
302 10
                break;
303 10
            case 2:
304 10
                [$fkTable, $fkName] = $_vars;
305
306
                break;
307
            default:
308
                [$fkTable] = $_vars;
309
310
                break;
311
        }
312
313
        return [
314
            'table' => $fkTable,
315
            'name' => $fkName,
316
            'id' => $fkId,
317
        ];
318
    }
319
}
320