Column::render()   B
last analyzed

Complexity

Conditions 8
Paths 15

Size

Total Lines 30
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 8

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 8
eloc 18
nc 15
nop 1
dl 0
loc 30
ccs 11
cts 11
cp 1
crap 8
rs 8.4444
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
     * Parse field definition into table definition.
41
     *
42 24
     * @throws ColumnException
43
     *
44 24
     */
45
    public static function parse(Field $field): self
46
    {
47
        $column = new self();
48
        $column->field = $field;
49
50
        if (!preg_match(self::DEFINITION, $field->getType(), $type)) {
51
            throw new ColumnException("Invalid column type declaration in `{$field->getType()}`");
52 24
        }
53
54 24
        $column->type = $type['type'];
55
        if (!empty($type['options'])) {
56
            $column->typeOptions = array_map('trim', explode(',', $type['options'] ?? ''));
57
        }
58
59
        return $column;
60 528
    }
61
62 528
    /**
63
     * Get column name.
64
     *
65
     * @psalm-suppress UnusedMethod
66
     */
67
    public function getName(): string
68 520
    {
69
        return $this->field->getColumn();
70 520
    }
71 8
72
    /**
73
     * Get column type.
74 512
     *
75
     * @psalm-suppress UnusedMethod
76
     */
77
    public function getType(): string
78
    {
79
        return $this->type;
80 528
    }
81
82 528
    public function isPrimary(): bool
83 408
    {
84
        return $this->field->isPrimary() || in_array($this->type, ['primary', 'bigPrimary']);
85
    }
86 496
87
    public function isNullable(): bool
88
    {
89
        if ($this->hasDefault() && $this->getDefault() === null) {
90
            return true;
91
        }
92
93
        return $this->hasOption(self::OPT_NULLABLE) && !$this->isPrimary();
94 24
    }
95
96 24
    public function hasDefault(): bool
97 8
    {
98
        if ($this->isPrimary()) {
99
            return false;
100 16
        }
101
102
        return $this->hasOption(self::OPT_DEFAULT);
103
    }
104
105
    /**
106
     * @return mixed
107
     * @throws ColumnException
108
     *
109
     */
110 496
    public function getDefault()
111
    {
112 496
        if (!$this->hasDefault()) {
113
            throw new ColumnException("No default value on `{$this->field->getColumn()}`");
114
        }
115
116 496
        return $this->field->getOptions()->get(self::OPT_DEFAULT);
117
    }
118
119
    /**
120
     * Render column definition.
121
     *
122
     * @throws ColumnException
123
     */
124
    public function render(AbstractColumn $column): void
125 496
    {
126 112
        $column->nullable($this->isNullable());
127
128
        try {
129 496
            // bypassing call to AbstractColumn->type method (or specialized column method)
130 8
            if (\method_exists($column, $this->type) && $this->typeOptions !== []) {
131 8
                call_user_func_array([$column, $this->type], $this->typeOptions);
132
            } else {
133
                call_user_func_array([$column, 'type'], \array_merge([$this->type], $this->typeOptions));
134 488
            }
135
        } catch (\Throwable $e) {
136 48
            throw new ColumnException(
137
                "Invalid column type definition in '{$column->getTable()}'.'{$column->getName()}'",
138 488
                (int) $e->getCode(),
139
                $e,
140
            );
141
        }
142
143
        if ($this->isNullable()) {
144
            $column->defaultValue(null);
145
        }
146
147
        if ($this->hasDefault() && $this->getDefault() !== null) {
148
            $column->defaultValue($this->getDefault());
149 536
        } elseif ($this->hasOption(self::OPT_CAST_DEFAULT)) {
150
            $column->defaultValue($this->castDefault($column));
151 536
        }
152 536
153
        $column->setAttributes(\iterator_to_array($this->field->getAttributes()));
154 536
    }
155 8
156
    /**
157
     *
158 528
     * @return bool|float|int|string
159 528
     */
160 384
    private function castDefault(AbstractColumn $column)
161
    {
162
        if (in_array($column->getAbstractType(), ['timestamp', 'datetime', 'time', 'date'])) {
163 528
            return 0;
164
        }
165
166
        if ($column->getAbstractType() === 'enum') {
167
            // we can use first enum value as default
168
            return $column->getEnumValues()[0];
169
        }
170
171 48
        switch ($column->getType()) {
172
            case AbstractColumn::INT:
173 48
                return 0;
174 8
            case AbstractColumn::FLOAT:
175
                return 0.0;
176
            case AbstractColumn::BOOL:
177 40
                return false;
178
        }
179 8
180
        return '';
181
    }
182 32
183 32
    private function hasOption(string $option): bool
184 10
    {
185 22
        return $this->field->getOptions()->has($option);
186 8
    }
187
}
188