Passed
Push — 2.x ( b60830...92ccd4 )
by Aleksei
13:13
created

Column::render()   B

Complexity

Conditions 7
Paths 15

Size

Total Lines 34
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 7.9295

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 7
eloc 19
nc 15
nop 1
dl 0
loc 34
ccs 11
cts 15
cp 0.7332
crap 7.9295
rs 8.8333
c 1
b 0
f 0
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Cycle\Schema\Table;
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\Schema\Definition\Field;
9
use Cycle\Schema\Exception\ColumnException;
10
11
/**
12
 * Carries information about column definition.
13
 *
14
 * @internal
15
 */
16
final class Column
17
{
18
    // default column value
19
    public const OPT_DEFAULT = 'default';
20
21
    // column can automatically define default value
22
    public const OPT_CAST_DEFAULT = 'castDefault';
23
24
    // column can be nullable
25
    public const OPT_NULLABLE = 'nullable';
26
27
    // provides ability to define complex types using string notation, i.e. string(32)
28
    private const DEFINITION = '/(?P<type>[a-z]+)(?: *\((?P<options>[^\)]+)\))?/i';
29
30
    /** @var Field */
31
    private $field;
32
33
    /** @var string */
34
    private $type;
35
36
    /** @var array */
37
    private $typeOptions = [];
38
39
    /**
40
     * @return string
41
     */
42 24
    public function getName(): string
43
    {
44 24
        return $this->field->getColumn();
45
    }
46
47
    /**
48
     * Get column type.
49
     *
50
     * @return string
51
     */
52 24
    public function getType(): string
53
    {
54 24
        return $this->type;
55
    }
56
57
    /**
58
     * @return bool
59
     */
60 528
    public function isPrimary(): bool
61
    {
62 528
        return $this->field->isPrimary() || in_array($this->type, ['primary', 'bigPrimary']);
63
    }
64
65
    /**
66
     * @return bool
67
     */
68 520
    public function isNullable(): bool
69
    {
70 520
        if ($this->hasDefault() && $this->getDefault() === null) {
71 8
            return true;
72
        }
73
74 512
        return $this->hasOption(self::OPT_NULLABLE) && !$this->isPrimary();
75
    }
76
77
    /**
78
     * @return bool
79
     */
80 528
    public function hasDefault(): bool
81
    {
82 528
        if ($this->isPrimary()) {
83 408
            return false;
84
        }
85
86 496
        return $this->hasOption(self::OPT_DEFAULT);
87
    }
88
89
    /**
90
     * @throws ColumnException
91
     *
92
     * @return mixed
93
     */
94 24
    public function getDefault()
95
    {
96 24
        if (!$this->hasDefault()) {
97 8
            throw new ColumnException("No default value on `{$this->field->getColumn()}`");
98
        }
99
100 16
        return $this->field->getOptions()->get(self::OPT_DEFAULT);
101
    }
102
103
    /**
104
     * Render column definition.
105
     *
106
     * @param AbstractColumn $column
107
     *
108
     * @throws ColumnException
109
     */
110 496
    public function render(AbstractColumn $column): void
111
    {
112 496
        $column->nullable($this->isNullable());
113
114
        try {
115
            // bypassing call to AbstractColumn->type method (or specialized column method)
116 496
            if (\method_exists($column, $this->type)) {
117
                call_user_func_array([$column, $this->type], $this->typeOptions);
118
            } else {
119
                call_user_func_array([$column, 'type'], \array_merge([$this->type], $this->typeOptions));
120
            }
121
        } catch (\Throwable $e) {
122
            throw new ColumnException(
123
                "Invalid column type definition in '{$column->getTable()}'.'{$column->getName()}'",
124
                $e->getCode(),
125 496
                $e
126 112
            );
127
        }
128
129 496
        if ($this->isNullable()) {
130 8
            $column->defaultValue(null);
131 8
        }
132
133
        if ($this->hasDefault() && $this->getDefault() !== null) {
134 488
            $column->defaultValue($this->getDefault());
135
            return;
136 48
        }
137
138 488
        if ($this->hasOption(self::OPT_CAST_DEFAULT)) {
139
            // cast default value
140
            $column->defaultValue($this->castDefault($column));
141
        }
142
143
        $column->setAttributes(\iterator_to_array($this->field->getAttributes()));
144
    }
145
146
    /**
147
     * Parse field definition into table definition.
148
     *
149 536
     * @param Field $field
150
     *
151 536
     * @throws ColumnException
152 536
     *
153
     * @return Column
154 536
     */
155 8
    public static function parse(Field $field): self
156
    {
157
        $column = new self();
158 528
        $column->field = $field;
159 528
160 384
        if (!preg_match(self::DEFINITION, $field->getType(), $type)) {
161
            throw new ColumnException("Invalid column type declaration in `{$field->getType()}`");
162
        }
163 528
164
        $column->type = $type['type'];
165
        if (!empty($type['options'])) {
166
            $column->typeOptions = array_map('trim', explode(',', $type['options'] ?? ''));
167
        }
168
169
        return $column;
170
    }
171 48
172
    /**
173 48
     * @param AbstractColumn $column
174 8
     *
175
     * @return bool|float|int|string
176
     */
177 40
    private function castDefault(AbstractColumn $column)
178
    {
179 8
        if (in_array($column->getAbstractType(), ['timestamp', 'datetime', 'time', 'date'])) {
180
            return 0;
181
        }
182 32
183 32
        if ($column->getAbstractType() === 'enum') {
184 10
            // we can use first enum value as default
185 22
            return $column->getEnumValues()[0];
186 8
        }
187 14
188 6
        switch ($column->getType()) {
189
            case AbstractColumn::INT:
190
                return 0;
191 8
            case AbstractColumn::FLOAT:
192
                return 0.0;
193
            case AbstractColumn::BOOL:
194
                return false;
195
        }
196
197
        return '';
198
    }
199 528
200
    /**
201 528
     * @param string $option
202
     *
203
     * @return bool
204
     */
205
    private function hasOption(string $option): bool
206
    {
207
        return $this->field->getOptions()->has($option);
208
    }
209
}
210