Passed
Push — 1.x ( da112a...e8ec08 )
by Aleksei
18:38 queued 03:42
created

RegistryModifier::addBigIntegerColumn()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 10
CRAP Score 3

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 13
nc 3
nop 3
dl 0
loc 24
ccs 10
cts 10
cp 1
crap 3
rs 9.8333
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\ORM\Entity\Behavior\Schema;
6
7
use Cycle\Database\Schema\AbstractColumn;
0 ignored issues
show
Bug introduced by
The type Cycle\Database\Schema\AbstractColumn was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
8
use Cycle\Database\Schema\AbstractTable;
9
use Cycle\ORM\Entity\Behavior\Exception\BehaviorCompilationException;
10
use Cycle\ORM\Parser\Typecast;
11
use Cycle\ORM\Parser\TypecastInterface;
12
use Cycle\ORM\SchemaInterface;
13
use Cycle\Schema\Defaults;
14
use Cycle\Schema\Definition\Entity;
15
use Cycle\Schema\Definition\Field;
16
use Cycle\Schema\Definition\Map\FieldMap;
17
use Cycle\Schema\Registry;
18
19
/**
20
 * @internal
21
 */
22
class RegistryModifier
23
{
24
    protected const DEFINITION = '/(?P<type>[a-z]+)(?: *\((?P<options>[^\)]+)\))?/i';
25
    protected const INTEGER_TYPES = [
26
        'int',
27
        'smallint',
28
        'tinyint',
29
        'bigint',
30
        'integer',
31 160
        'tinyInteger',
32
        'smallInteger',
33 160
        'bigInteger',
34 160
    ];
35 160
    protected const BIG_INTEGER_TYPES = [
36
        'bigint',
37
        'bigInteger',
38 112
    ];
39
    protected const DATETIME_TYPES = ['datetime', 'datetime2'];
40 112
    protected const INT_COLUMN = AbstractColumn::INT;
41 64
    protected const STRING_COLUMN = AbstractColumn::STRING;
42 8
    protected const BIG_INTEGER_COLUMN = 'bigInteger';
43
    protected const DATETIME_COLUMN = 'datetime';
44 56
    protected const UUID_COLUMN = 'uuid';
45
46 48
    protected FieldMap $fields;
47
    protected AbstractTable $table;
48
    protected Entity $entity;
49 96
    protected Defaults $defaults;
50
51 96
    public function __construct(Registry $registry, string $role)
52
    {
53
        $this->entity = $registry->getEntity($role);
54 96
        $this->fields = $this->entity->getFields();
55
        $this->table = $registry->getTableSchema($this->entity);
56
        $this->defaults = $registry->getDefaults();
57 56
    }
58
59 56
    public static function isIntegerType(string $type): bool
60 40
    {
61
        \preg_match(self::DEFINITION, $type, $matches);
62
63 40
        return \in_array($matches['type'], self::INTEGER_TYPES, true);
64
    }
65 40
66
    public static function isBigIntegerType(string $type): bool
67
    {
68 56
        \preg_match(self::DEFINITION, $type, $matches);
69
70 56
        return \in_array($matches['type'], self::BIG_INTEGER_TYPES, true);
71
    }
72
73 48
    public static function isDatetimeType(string $type): bool
74
    {
75 48
        \preg_match(self::DEFINITION, $type, $matches);
76 40
77
        return \in_array($matches['type'], self::DATETIME_TYPES, true);
78
    }
79 40
80
    public static function isStringType(string $type): bool
81 40
    {
82
        \preg_match(self::DEFINITION, $type, $matches);
83
84 48
        return $matches['type'] === 'string';
85
    }
86 48
87
    public static function isUuidType(string $type): bool
88
    {
89
        \preg_match(self::DEFINITION, $type, $matches);
90
91
        return $matches['type'] === 'uuid';
92 32
    }
93
94 32
    public function addDatetimeColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn
95
    {
96
        if ($this->fields->has($fieldName)) {
97
            if (!static::isDatetimeType($this->fields->get($fieldName)->getType())) {
98
                throw new BehaviorCompilationException(\sprintf('Field %s must be of type datetime.', $fieldName));
99
            }
100
            $this->validateColumnName($fieldName, $columnName);
101
            $this->fields->get($fieldName)->setGenerated($generated);
102
103 32
            return $this->table->column($columnName);
104
        }
105 32
106
        $field = (new Field())
107
            ->setColumn($columnName)
108 104
            ->setType('datetime')
109
            ->setTypecast('datetime')
110 104
            ->setGenerated($generated);
111 64
        $this->fields->set($fieldName, $field);
112
113
        return $this->table->column($columnName)->type(self::DATETIME_COLUMN);
114 88
    }
115
116
    public function addIntegerColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn
117
    {
118
        if ($this->fields->has($fieldName)) {
119
            if (!static::isIntegerType($this->fields->get($fieldName)->getType())) {
120 24
                throw new BehaviorCompilationException(\sprintf('Field %s must be of type integer.', $fieldName));
121
            }
122 24
            $this->validateColumnName($fieldName, $columnName);
123 16
            $this->fields->get($fieldName)->setGenerated($generated);
124
125
            return $this->table->column($columnName);
126 24
        }
127 24
128 16
        $field = (new Field())
129 16
            ->setColumn($columnName)
130
            ->setType('integer')
131
            ->setTypecast('int')
132 16
            ->setGenerated($generated);
133 16
        $this->fields->set($fieldName, $field);
134 16
135
        return $this->table->column($columnName)->type(self::INT_COLUMN);
136 16
    }
137
138
    public function addBigIntegerColumn(
139
        string $columnName,
140
        string $fieldName,
141
        int|null $generated = null,
142 96
    ): AbstractColumn {
143
        if ($this->fields->has($fieldName)) {
144 96
            if (! static::isBigIntegerType($this->fields->get($fieldName)->getType())) {
145
                throw new BehaviorCompilationException(\sprintf('Field %s must be of type big integer.', $fieldName));
146 96
            }
147 8
            $this->validateColumnName($fieldName, $columnName);
148 8
            $this->fields->get($fieldName)->setGenerated($generated);
149 8
150 8
            return $this->table->column($columnName);
151
        }
152 8
153
        $field = (new Field())
154
            ->setColumn($columnName)
155
            ->setType(self::BIG_INTEGER_COLUMN)
156
            ->setTypecast('int')
157
            ->setGenerated($generated);
158
159 104
        $this->fields->set($fieldName, $field);
160
161 104
        return $this->table->column($columnName)->type(self::BIG_INTEGER_COLUMN);
162
    }
163 64
164 64
    public function addStringColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn
165
    {
166
        if ($this->fields->has($fieldName)) {
167 40
            if (!static::isStringType($this->fields->get($fieldName)->getType())) {
168 40
                throw new BehaviorCompilationException(\sprintf('Field %s must be of type string.', $fieldName));
169
            }
170
            $this->validateColumnName($fieldName, $columnName);
171 40
            $this->fields->get($fieldName)->setGenerated($generated);
172
173
            return $this->table->column($columnName);
174
        }
175
176
        $field = (new Field())->setColumn($columnName)->setType('string')->setGenerated($generated);
177 40
        $this->fields->set($fieldName, $field);
178
179
        return $this->table->column($columnName)->type(self::STRING_COLUMN);
180
    }
181
182
    /**
183
     * @throws BehaviorCompilationException
184
     */
185
    public function addUuidColumn(string $columnName, string $fieldName, int|null $generated = null): AbstractColumn
186
    {
187
        if ($this->fields->has($fieldName)) {
188
            if (!static::isUuidType($this->fields->get($fieldName)->getType())) {
189
                throw new BehaviorCompilationException(\sprintf('Field %s must be of type uuid.', $fieldName));
190
            }
191
            $this->validateColumnName($fieldName, $columnName);
192
            $this->fields->get($fieldName)->setGenerated($generated);
193
194
            return $this->table->column($columnName);
195
        }
196
197
        $field = (new Field())->setColumn($columnName)->setType('uuid')->setGenerated($generated);
198
        $this->fields->set($fieldName, $field);
199
200
        return $this->table->column($columnName)->type(self::UUID_COLUMN);
201
    }
202
203
    public function findColumnName(string $fieldName, ?string $columnName): ?string
204
    {
205
        if ($columnName !== null) {
206
            return $columnName;
207
        }
208
209
        return $this->fields->has($fieldName) ? $this->fields->get($fieldName)->getColumn() : null;
210
    }
211
212
    /**
213
     * @param class-string<TypecastInterface> $handler
0 ignored issues
show
Documentation Bug introduced by
The doc comment class-string<TypecastInterface> at position 0 could not be parsed: Unknown type name 'class-string' at position 0 in class-string<TypecastInterface>.
Loading history...
214
     */
215
    public function setTypecast(Field $field, array|string|null $rule, string $handler = Typecast::class): Field
216
    {
217
        if ($field->getTypecast() === null) {
218
            $field->setTypecast($rule);
219
        }
220
221
        $defaultHandlers = $this->defaults[SchemaInterface::TYPECAST_HANDLER] ?? [];
222
        if (!\is_array($defaultHandlers)) {
223
            $defaultHandlers = [$defaultHandlers];
224
        }
225
226
        $handlers = $this->entity->getTypecast() ?? [];
227
        if (!\is_array($handlers)) {
228
            $handlers = [$handlers];
229
        }
230
231
        if (!\in_array($handler, $handlers, true) && !\in_array($handler, $defaultHandlers, true)) {
0 ignored issues
show
Bug introduced by
It seems like $handlers can also be of type string; however, parameter $haystack of in_array() 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

231
        if (!\in_array($handler, /** @scrutinizer ignore-type */ $handlers, true) && !\in_array($handler, $defaultHandlers, true)) {
Loading history...
232
            $this->entity->setTypecast(\array_merge($handlers, [$handler]));
0 ignored issues
show
Bug introduced by
It seems like $handlers can also be of type string; however, parameter $arrays of array_merge() 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

232
            $this->entity->setTypecast(\array_merge(/** @scrutinizer ignore-type */ $handlers, [$handler]));
Loading history...
233
        }
234
235
        return $field;
236
    }
237
238
    /**
239
     * @throws BehaviorCompilationException
240
     */
241
    protected function validateColumnName(string $fieldName, string $columnName): void
242
    {
243
        $field = $this->fields->get($fieldName);
244
245
        if ($field->getColumn() !== $columnName) {
246
            throw new BehaviorCompilationException(
247
                \sprintf(
248
                    'Ambiguous column name definition. '
249
                    . 'The `%s` field already linked with the `%s` column but the behavior expects `%s`.',
250
                    $fieldName,
251
                    $field->getColumn(),
252
                    $columnName,
253
                ),
254
            );
255
        }
256
    }
257
258
    /**
259
     * @deprecated since v1.2
260
     */
261
    protected function isType(string $type, string $fieldName, string $columnName): bool
262
    {
263
        if ($type === self::DATETIME_COLUMN) {
264
            return
265
                $this->table->column($columnName)->getInternalType() === self::DATETIME_COLUMN ||
266
                $this->fields->get($fieldName)->getType() === self::DATETIME_COLUMN;
267
        }
268
269
        if ($type === self::INT_COLUMN) {
270
            return $this->table->column($columnName)->getType() === self::INT_COLUMN;
271
        }
272
273
        if ($type === self::UUID_COLUMN) {
274
            return
275
                $this->table->column($columnName)->getInternalType() === self::UUID_COLUMN ||
276
                $this->fields->get($fieldName)->getType() === self::UUID_COLUMN;
277
        }
278
279
        return $this->table->column($columnName)->getType() === $type;
280
    }
281
}
282