Passed
Push — 1.x ( 5002a5...60cc56 )
by butschster
02:42
created

RegistryModifier::addDatetimeColumn()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 17
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 9
CRAP Score 3

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 2
dl 0
loc 17
ccs 9
cts 9
cp 1
crap 3
rs 9.9666
c 0
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\Schema\Definition\Entity;
13
use Cycle\Schema\Definition\Field;
14
use Cycle\Schema\Definition\Map\FieldMap;
15
use Cycle\Schema\Registry;
16
17
/**
18
 * @internal
19
 */
20
class RegistryModifier
21
{
22
    protected const INT_COLUMN = AbstractColumn::INT;
23
    protected const STRING_COLUMN = AbstractColumn::STRING;
24
    protected const DATETIME_COLUMN = 'datetime';
25
    protected const UUID_COLUMN = 'uuid';
26
27
    protected FieldMap $fields;
28
    protected AbstractTable $table;
29
    protected Entity $entity;
30
31 160
    public function __construct(Registry $registry, string $role)
32
    {
33 160
        $this->entity = $registry->getEntity($role);
34 160
        $this->fields = $this->entity->getFields();
35 160
        $this->table = $registry->getTableSchema($this->entity);
36
    }
37
38 112
    public function addDatetimeColumn(string $columnName, string $fieldName): AbstractColumn
39
    {
40 112
        if ($this->fields->has($fieldName)) {
41 64
            if (!$this->isType(self::DATETIME_COLUMN, $fieldName, $columnName)) {
42 8
                throw new BehaviorCompilationException(sprintf('Field %s must be of type datetime.', $fieldName));
43
            }
44 56
            $this->validateColumnName($fieldName, $columnName);
45
46 48
            return $this->table->column($columnName);
47
        }
48
49 96
        $this->fields->set(
50
            $fieldName,
51 96
            (new Field())->setColumn($columnName)->setType('datetime')->setTypecast('datetime')
52
        );
53
54 96
        return $this->table->column($columnName)->type(self::DATETIME_COLUMN);
55
    }
56
57 56
    public function addIntegerColumn(string $columnName, string $fieldName): AbstractColumn
58
    {
59 56
        if ($this->fields->has($fieldName)) {
60 40
            if (!$this->isType(self::INT_COLUMN, $fieldName, $columnName)) {
61
                throw new BehaviorCompilationException(sprintf('Field %s must be of type integer.', $fieldName));
62
            }
63 40
            $this->validateColumnName($fieldName, $columnName);
64
65 40
            return $this->table->column($columnName);
66
        }
67
68 56
        $this->fields->set($fieldName, (new Field())->setColumn($columnName)->setType('integer')->setTypecast('int'));
69
70 56
        return $this->table->column($columnName)->type(self::INT_COLUMN);
71
    }
72
73 48
    public function addStringColumn(string $columnName, string $fieldName): AbstractColumn
74
    {
75 48
        if ($this->fields->has($fieldName)) {
76 40
            if (!$this->isType(self::STRING_COLUMN, $fieldName, $columnName)) {
77
                throw new BehaviorCompilationException(sprintf('Field %s must be of type string.', $fieldName));
78
            }
79 40
            $this->validateColumnName($fieldName, $columnName);
80
81 40
            return $this->table->column($columnName);
82
        }
83
84 48
        $this->fields->set($fieldName, (new Field())->setColumn($columnName)->setType('string'));
85
86 48
        return $this->table->column($columnName)->type(self::STRING_COLUMN);
87
    }
88
89
    /**
90
     * @throws BehaviorCompilationException
91
     */
92 32
    public function addUuidColumn(string $columnName, string $fieldName): AbstractColumn
93
    {
94 32
        if ($this->fields->has($fieldName)) {
95
            if (!$this->isType(self::UUID_COLUMN, $fieldName, $columnName)) {
96
                throw new BehaviorCompilationException(sprintf('Field %s must be of type uuid.', $fieldName));
97
            }
98
            $this->validateColumnName($fieldName, $columnName);
99
100
            return $this->table->column($columnName);
101
        }
102
103 32
        $this->fields->set($fieldName, (new Field())->setColumn($columnName)->setType('uuid'));
104
105 32
        return $this->table->column($columnName)->type(self::UUID_COLUMN);
106
    }
107
108 104
    public function findColumnName(string $fieldName, ?string $columnName): ?string
109
    {
110 104
        if ($columnName !== null) {
111 64
            return $columnName;
112
        }
113
114 88
        return $this->fields->has($fieldName) ? $this->fields->get($fieldName)->getColumn() : null;
115
    }
116
117
    /**
118
     * @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...
119
     */
120 24
    public function setTypecast(Field $field, array|string|null $rule, string $handler = Typecast::class): Field
121
    {
122 24
        if ($field->getTypecast() === null) {
123 16
            $field->setTypecast($rule);
124
        }
125
126 24
        $handlers = $this->entity->getTypecast();
127 24
        if ($handlers === null) {
128 16
            $this->entity->setTypecast($handler);
129 16
            return $field;
130
        }
131
132 16
        $handlers = (array) $handlers;
133 16
        $handlers[] = $handler;
134 16
        $this->entity->setTypecast(array_unique($handlers));
135
136 16
        return $field;
137
    }
138
139
    /**
140
     * @throws BehaviorCompilationException
141
     */
142 96
    protected function validateColumnName(string $fieldName, string $columnName): void
143
    {
144 96
        $field = $this->fields->get($fieldName);
145
146 96
        if ($field->getColumn() !== $columnName) {
147 8
            throw new BehaviorCompilationException(
148 8
                sprintf(
149 8
                    'Ambiguous column name definition. '
150 8
                    . 'The `%s` field already linked with the `%s` column but the behavior expects `%s`.',
151
                    $fieldName,
152 8
                    $field->getColumn(),
153
                    $columnName
154
                )
155
            );
156
        }
157
    }
158
159 104
    protected function isType(string $type, string $fieldName, string $columnName): bool
160
    {
161 104
        if ($type === self::DATETIME_COLUMN) {
162
            return
163 64
                $this->table->column($columnName)->getInternalType() === self::DATETIME_COLUMN ||
164 64
                $this->fields->get($fieldName)->getType() === self::DATETIME_COLUMN;
165
        }
166
167 40
        if ($type === self::INT_COLUMN) {
168 40
            return $this->table->column($columnName)->getType() === self::INT_COLUMN;
169
        }
170
171 40
        if ($type === self::UUID_COLUMN) {
172
            return
173
                $this->table->column($columnName)->getInternalType() === self::UUID_COLUMN ||
174
                $this->fields->get($fieldName)->getType() === self::UUID_COLUMN;
175
        }
176
177 40
        return $this->table->column($columnName)->getType() === $type;
178
    }
179
}
180