SchemaAttribute::isFk()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 3
rs 10
ccs 2
cts 2
cp 1
crap 1
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\Action;
14
use FlexPHP\Schema\Constants\Keyword;
15
use FlexPHP\Schema\Constants\Rule;
16
use FlexPHP\Schema\Exception\InvalidSchemaAttributeException;
17
use FlexPHP\Schema\Validations\SchemaAttributeLogicValidation;
18
use FlexPHP\Schema\Validations\SchemaAttributeValidation;
19
20
final class SchemaAttribute implements SchemaAttributeInterface
21
{
22
    private string $name;
23
24
    private string $dataType;
25
26
    /**
27
     * @var array<string, mixed>
28
     */
29
    private array $constraints = [];
30
31
    /**
32
     * @param mixed $constraints
33
     */
34 290
    public function __construct(string $name, string $dataType, $constraints = null)
35
    {
36 290
        $this->setName($name);
37 290
        $this->setDataType($dataType);
38 290
        $this->setConstraints($constraints);
39
40 290
        $this->validate();
41 191
    }
42
43 290
    public function name(): string
44
    {
45 290
        return $this->name;
46
    }
47
48 290
    public function dataType(): string
49
    {
50 290
        return $this->dataType;
51
    }
52
53 290
    public function constraints(): array
54
    {
55 290
        return $this->constraints;
56
    }
57
58 6
    public function type(): ?string
59
    {
60 6
        return $this->constraints[Rule::TYPE] ?? null;
61
    }
62
63 40
    public function isRequired(): bool
64
    {
65 40
        return (bool)($this->constraints[Rule::REQUIRED] ?? false);
66
    }
67
68 150
    public function minLength(): ?int
69
    {
70 150
        return $this->constraints[Rule::MINLENGTH] ?? null;
71
    }
72
73 145
    public function maxLength(): ?int
74
    {
75 145
        return $this->constraints[Rule::MAXLENGTH] ?? null;
76
    }
77
78 78
    public function minCheck(): ?int
79
    {
80 78
        return $this->constraints[Rule::MINCHECK] ?? null;
81
    }
82
83 77
    public function maxCheck(): ?int
84
    {
85 77
        return $this->constraints[Rule::MAXCHECK] ?? null;
86
    }
87
88 163
    public function min(): ?int
89
    {
90 163
        return $this->constraints[Rule::MIN] ?? null;
91
    }
92
93 160
    public function max(): ?int
94
    {
95 160
        return $this->constraints[Rule::MAX] ?? null;
96
    }
97
98 6
    public function equalTo(): ?string
99
    {
100 6
        return $this->constraints[Rule::EQUALTO] ?? null;
101
    }
102
103 256
    public function isPk(): bool
104
    {
105 256
        return (bool)($this->constraints[Rule::PRIMARYKEY] ?? false);
106
    }
107
108 251
    public function isAi(): bool
109
    {
110 251
        return (bool)($this->constraints[Rule::AUTOINCREMENT] ?? false);
111
    }
112
113 46
    public function isFk(): bool
114
    {
115 46
        return (bool)($this->constraints[Rule::FOREIGNKEY] ?? false);
116
    }
117
118 9
    public function fkTable(): ?string
119
    {
120 9
        return $this->constraints[Rule::FOREIGNKEY]['table'] ?? null;
121
    }
122
123 9
    public function fkId(): ?string
124
    {
125 9
        return $this->constraints[Rule::FOREIGNKEY]['id'] ?? null;
126
    }
127
128 9
    public function fkName(): ?string
129
    {
130 9
        return $this->constraints[Rule::FOREIGNKEY]['name'] ?? null;
131
    }
132
133 252
    public function isCa(): bool
134
    {
135 252
        return (bool)($this->constraints[Rule::CREATEDAT] ?? false);
136
    }
137
138 251
    public function isUa(): bool
139
    {
140 251
        return (bool)($this->constraints[Rule::UPDATEDAT] ?? false);
141
    }
142
143 249
    public function isCb(): bool
144
    {
145 249
        return (bool)($this->constraints[Rule::CREATEDBY] ?? false);
146
    }
147
148 247
    public function isUb(): bool
149
    {
150 247
        return (bool)($this->constraints[Rule::UPDATEDBY] ?? false);
151
    }
152
153 252
    public function isBlameAt(): bool
154
    {
155 252
        return $this->isCa() || $this->isUa();
156
    }
157
158 249
    public function isBlameBy(): bool
159
    {
160 249
        return $this->isCb() || $this->isUb();
161
    }
162
163 188
    public function isBlame(): bool
164
    {
165 188
        return $this->isBlameAt() || $this->isBlameBy();
166
    }
167
168 3
    public function filter(): ?string
169
    {
170 3
        return $this->constraints[Rule::FILTER] ?? null;
171
    }
172
173 221
    public function format(): ?string
174
    {
175 221
        return $this->constraints[Rule::FORMAT] ?? null;
176
    }
177
178 13
    public function isFormat(string $format): bool
179
    {
180 13
        return $this->format() === $format;
181
    }
182
183 7
    public function trim(): bool
184
    {
185 7
        return (bool)($this->constraints[Rule::TRIM] ?? false);
186
    }
187
188 189
    public function fchars(): ?int
189
    {
190 189
        return $this->constraints[Rule::FCHARS] ?? null;
191
    }
192
193 184
    public function fkcheck(): bool
194
    {
195 184
        return (bool)($this->constraints[Rule::FKCHECK] ?? false);
196
    }
197
198 7
    public function link(): bool
199
    {
200 7
        return (bool)($this->constraints[Rule::LINK] ?? false);
201
    }
202
203 184
    public function show(): array
204
    {
205 184
        $default = $this->isBlame()
206 42
            ? Action::READ
207 184
            : Action::ALL;
208
209 184
        $hideConstraint = $this->constraints[Rule::HIDE] ?? '';
210
211 184
        if (\strpos($hideConstraint, Action::ALL) !== false) {
212 2
            $default = \str_replace(Action::ALL, '', $default);
213
        }
214
215 184
        if (\strpos($hideConstraint, Action::READ) !== false) {
216 6
            $default = \str_replace(Action::READ, '', $default);
217
        }
218
219 184
        return \explode(',', $this->constraints[Rule::SHOW] ?? $default);
220
    }
221
222 176
    public function hide(): array
223
    {
224 176
        $default = $this->isBlame()
225 38
            ? Action::INDEX . Action::CREATE . Action::UPDATE . Action::DELETE
226 176
            : '';
227
228 176
        return \explode(',', $this->constraints[Rule::HIDE] ?? $default);
229
    }
230
231
    /**
232
     * @return mixed
233
     */
234 177
    public function default()
235
    {
236 177
        if (!isset($this->constraints[Rule::DEFAULT])) {
237 158
            return null;
238
        }
239
240 19
        return $this->constraints[Rule::DEFAULT];
241
    }
242
243 184
    public function usedIn(string $action): bool
244
    {
245 184
        return \in_array($action, $this->show(), true);
246
    }
247
248 290
    public function properties(): array
249
    {
250
        return [
251 290
            Keyword::NAME => $this->name(),
252 290
            Keyword::DATATYPE => $this->dataType(),
253 290
            Keyword::CONSTRAINTS => $this->constraints(),
254
        ];
255
    }
256
257 231
    public function typeHint(): string
258
    {
259
        $typeHintByDataType = [
260 231
            'smallint' => 'int',
261
            'integer' => 'int',
262
            'float' => 'float',
263
            'double' => 'float',
264
            'bool' => 'bool',
265
            'boolean' => 'bool',
266
            'date' => '\DateTime',
267
            'date_immutable' => '\DateTimeImmutable',
268
            'datetime' => '\DateTime',
269
            'datetime_immutable' => '\DateTimeImmutable',
270
            'datetimetz' => '\DateTime',
271
            'datetimetz_immutable' => '\DateTimeImmutable',
272
            'time' => '\DateTime',
273
            'time_immutable' => '\DateTimeImmutable',
274
            'array' => 'array',
275
            'simple_array' => 'array',
276
            'json' => 'array',
277
        ];
278
279 231
        if (isset($typeHintByDataType[$this->dataType()])) {
280 141
            return $typeHintByDataType[$this->dataType()];
281
        }
282
283 104
        return 'string';
284
    }
285
286 290
    private function validate(): void
287
    {
288
        try {
289 290
            (new SchemaAttributeValidation($this->properties()))->validate();
290 283
            (new SchemaAttributeLogicValidation($this))->validate();
291 100
        } catch (Exception $exception) {
292 100
            throw new InvalidSchemaAttributeException(
293 100
                \sprintf('Attribute %s > %s', $this->name(), $exception->getMessage())
294
            );
295
        }
296 191
    }
297
298 290
    private function setName(string $name): void
299
    {
300 290
        $this->name = $name;
301 290
    }
302
303 290
    private function setDataType(string $dataType): void
304
    {
305 290
        $this->dataType = $dataType;
306 290
    }
307
308
    /**
309
     * @param mixed $constraints
310
     */
311 290
    private function setConstraints($constraints): void
312
    {
313 290
        if (!empty($constraints)) {
314 259
            if (\is_string($constraints)) {
315 208
                $this->setConstraintsFromString($constraints);
316
            } else {
317 51
                $this->setConstraintsFromArray($constraints);
318
            }
319
        }
320 290
    }
321
322 208
    private function setConstraintsFromString(string $constraints): void
323
    {
324 208
        $this->setConstraintsFromArray($this->getConstraintsFromString($constraints));
325 208
    }
326
327 259
    private function setConstraintsFromArray(array $constraints): void
328
    {
329 259
        $this->constraints = $this->getConstraintsCast($constraints);
330 259
    }
331
332 208
    private function getConstraintsFromString(string $constraints): array
333
    {
334 208
        $_constraints = \explode('|', $constraints);
335
336
        /** @var mixed $_constraint */
337 208
        foreach ($_constraints as $index => $_constraint) {
338 208
            $_rule = \explode(':', $_constraint);
339
340 208
            if (\count($_rule) === 2) {
341 167
                [$_name, $_options] = $_rule;
342
343 167
                if (Rule::FOREIGNKEY !== $_name && \strpos($_options, ',') !== false) { // Range
344 3
                    [$min, $max] = \explode(',', $_options);
345 3
                    $_options = ['min' => $min, 'max' => $max];
346 164
                } elseif (\preg_match('#^false$#i', $_options)) { // False as string
347 25
                    $_options = false;
348 153
                } elseif (\preg_match('#^true$#i', $_options)) { // True as string
349 25
                    $_options = true;
350
                }
351
352 167
                $_constraints[$_name] = $_options;
353
            } else {
354 73
                $_constraints[$_rule[0]] = true;
355
            }
356
357 208
            unset($_constraints[$index]);
358
        }
359
360 208
        return $_constraints;
361
    }
362
363 259
    private function getConstraintsCast(array $constraints): array
364
    {
365 259
        foreach ($constraints as $name => $value) {
366 259
            if (\is_int($name)) {
367 15
                $constraints[$value] = true;
368 15
                unset($constraints[$name]);
369 250
            } elseif ($name === Rule::CHECK || $name === Rule::LENGTH) {
370 4
                $constraints['min' . $name] = (int)$value['min'];
371 4
                $constraints['max' . $name] = (int)$value['max'];
372 4
                unset($constraints[$name]);
373 246
            } elseif ($name === Rule::FOREIGNKEY && \is_string($value)) {
374 29
                $constraints[$name] = $this->getFkOptions($value);
375 239
            } elseif ($name === Rule::DEFAULT && \is_string($value) && \preg_match('#^\d.\d#', $value)) {
376 2
                $constraints[$name] = (float)$value;
377
            } else {
378 237
                $constraints[$name] = \is_numeric($value) ? (int)$value : $value;
379
            }
380
        }
381
382 259
        return $constraints;
383
    }
384
385 29
    private function getFkOptions(string $constraint): array
386
    {
387 29
        $_vars = \explode(',', $constraint);
388 29
        $fkName = 'name';
389 29
        $fkId = 'id';
390
391 29
        switch (\count($_vars)) {
392 29
            case 3:
393 2
                [$fkTable, $fkName, $fkId] = $_vars;
394
395 2
                break;
396 27
            case 2:
397 23
                [$fkTable, $fkName] = $_vars;
398
399 23
                break;
400
            default:
401 4
                [$fkTable] = $_vars;
402
403 4
                break;
404
        }
405
406
        return [
407 29
            'table' => $fkTable,
408 29
            'name' => $fkName,
409 29
            'id' => $fkId,
410
        ];
411
    }
412
}
413