Issues (82)

src/Table/Column.php (1 issue)

Labels
Severity
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
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
    public function getName(): string
63
    {
64
        return $this->field->getColumn();
65
    }
66
67
    /**
68 520
     * Get column type.
69
     *
70 520
     */
71 8
    public function getType(): string
72
    {
73
        return $this->type;
74 512
    }
75
76
    public function isPrimary(): bool
77
    {
78
        return $this->field->isPrimary() || in_array($this->type, ['primary', 'bigPrimary']);
79
    }
80 528
81
    public function isNullable(): bool
82 528
    {
83 408
        if ($this->hasDefault() && $this->getDefault() === null) {
84
            return true;
85
        }
86 496
87
        return $this->hasOption(self::OPT_NULLABLE) && !$this->isPrimary();
88
    }
89
90
    public function hasDefault(): bool
91
    {
92
        if ($this->isPrimary()) {
93
            return false;
94 24
        }
95
96 24
        return $this->hasOption(self::OPT_DEFAULT);
97 8
    }
98
99
    /**
100 16
     * @return mixed
101
     * @throws ColumnException
102
     *
103
     */
104
    public function getDefault()
105
    {
106
        if (!$this->hasDefault()) {
107
            throw new ColumnException("No default value on `{$this->field->getColumn()}`");
108
        }
109
110 496
        return $this->field->getOptions()->get(self::OPT_DEFAULT);
111
    }
112 496
113
    /**
114
     * Render column definition.
115
     *
116 496
     * @throws ColumnException
117
     */
118
    public function render(AbstractColumn $column): void
119
    {
120
        $column->nullable($this->isNullable());
121
122
        try {
123
            // bypassing call to AbstractColumn->type method (or specialized column method)
124
            if (\method_exists($column, $this->type) && $this->typeOptions !== []) {
125 496
                call_user_func_array([$column, $this->type], $this->typeOptions);
126 112
            } else {
127
                call_user_func_array([$column, 'type'], \array_merge([$this->type], $this->typeOptions));
128
            }
129 496
        } catch (\Throwable $e) {
130 8
            throw new ColumnException(
131 8
                "Invalid column type definition in '{$column->getTable()}'.'{$column->getName()}'",
132
                (int) $e->getCode(),
133
                $e,
134 488
            );
135
        }
136 48
137
        if ($this->isNullable()) {
138 488
            $column->defaultValue(null);
139
        }
140
141
        if ($this->hasDefault() && $this->getDefault() !== null) {
142
            $column->defaultValue($this->getDefault());
143
            return;
144
        }
145
146
        if ($this->hasOption(self::OPT_CAST_DEFAULT)) {
147
            // cast default value
148
            $column->defaultValue($this->castDefault($column));
149 536
        }
150
151 536
        $column->setAttributes(\iterator_to_array($this->field->getAttributes()));
152 536
    }
153
154 536
    /**
155 8
     *
156
     * @return bool|float|int|string
157
     */
158 528
    private function castDefault(AbstractColumn $column)
159 528
    {
160 384
        if (in_array($column->getAbstractType(), ['timestamp', 'datetime', 'time', 'date'])) {
161
            return 0;
162
        }
163 528
164
        if ($column->getAbstractType() === 'enum') {
165
            // we can use first enum value as default
166
            return $column->getEnumValues()[0];
167
        }
168
169
        switch ($column->getType()) {
170
            case AbstractColumn::INT:
171 48
                return 0;
172
            case AbstractColumn::FLOAT:
173 48
                return 0.0;
174 8
            case AbstractColumn::BOOL:
175
                return false;
176
        }
177 40
178
        return '';
179 8
    }
180
181
    private function hasOption(string $option): bool
182 32
    {
183 32
        return $this->field->getOptions()->has($option);
184 10
    }
185
}
186