Passed
Pull Request — master (#808)
by Sergei
15:21 queued 13:01
created

buildReferenceDefinition()   A

Complexity

Conditions 6
Paths 17

Size

Total Lines 31
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 16
c 1
b 0
f 0
nc 17
nop 1
dl 0
loc 31
rs 9.1111
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Yiisoft\Db\QueryBuilder;
6
7
use Yiisoft\Db\Constraint\ForeignKeyConstraint;
8
use Yiisoft\Db\Expression\ExpressionInterface;
9
use Yiisoft\Db\Helper\DbStringHelper;
10
use Yiisoft\Db\Schema\Column\ColumnInterface;
11
use Yiisoft\Db\Schema\QuoterInterface;
12
13
/**
14
 * Builds column definition from {@see ColumnInterface} object. Column definition is a string that represents
15
 * the column type and all constraints associated with the column. For example: `VARCHAR(128) NOT NULL DEFAULT 'foo'`.
16
 *
17
 * You can use {@see ColumnDefinitionBuilder} class in the following way:
18
 * `(string) (new ColumnDefinitionBuilder($column));`
19
 */
20
class ColumnDefinitionBuilder implements ColumnDefinitionBuilderInterface
21
{
22
    protected array $clauses = [
23
        'type',
24
        'null',
25
        'primary_key',
26
        'auto_increment',
27
        'unique',
28
        'default',
29
        'comment',
30
        'check',
31
        'references',
32
        'extra',
33
    ];
34
35
    public function __construct(
36
        protected QueryBuilderInterface $queryBuilder,
37
        protected QuoterInterface $quoter,
38
    ) {
39
    }
40
41
    public function build(ColumnInterface $column): string
42
    {
43
        $result = '';
44
45
        foreach ($this->clauses as $clause) {
46
            $result .= match ($clause) {
47
                'type' => $this->buildType($column),
48
                'null' => $this->buildNull($column),
49
                'primary_key' => $this->buildPrimaryKey($column),
50
                'auto_increment' => $this->buildAutoIncrement($column),
51
                'unique' => $this->buildUnique($column),
52
                'default' => $this->buildDefault($column),
53
                'comment' => $this->buildComment($column),
54
                'check' => $this->buildCheck($column),
55
                'references' => $this->buildReferences($column),
56
                'extra' => $this->buildExtra($column),
57
                default => '',
58
            };
59
        }
60
61
        return $result;
62
    }
63
64
    protected function buildType(ColumnInterface $column): string
65
    {
66
        return (string) $column->getFullDbType();
67
    }
68
69
    /**
70
     * Builds the null or not null constraint for the column.
71
     *
72
     * @return string A string 'NOT NULL' if {@see ColumnInterface::allowNull} is false,
73
     * 'NULL' if {@see ColumnInterface::allowNull} is true or an empty string otherwise.
74
     */
75
    protected function buildNull(ColumnInterface $column): string
76
    {
77
        if ($column->isPrimaryKey()) {
78
            return '';
79
        }
80
81
        return match ($column->isAllowNull()) {
82
            true => ' NULL',
83
            false => ' NOT NULL',
84
            default => '',
85
        };
86
    }
87
88
    /**
89
     * Builds the primary key clause for column.
90
     *
91
     * @return string A string containing the PRIMARY KEY keyword.
92
     */
93
    public function buildPrimaryKey(ColumnInterface $column): string
94
    {
95
        return $column->isPrimaryKey() ? ' PRIMARY KEY' : '';
96
    }
97
98
    /**
99
     * Builds the auto increment clause for column. Default is empty string.
100
     *
101
     * @return string A string containing the AUTOINCREMENT keyword.
102
     */
103
    public function buildAutoIncrement(ColumnInterface $column): string
0 ignored issues
show
Unused Code introduced by
The parameter $column is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

103
    public function buildAutoIncrement(/** @scrutinizer ignore-unused */ ColumnInterface $column): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
104
    {
105
        return '';
106
    }
107
108
    /**
109
     * Builds the unique constraint for the column.
110
     *
111
     * @return string A string 'UNIQUE' if {@see isUnique} is true, otherwise it returns an empty string.
112
     */
113
    protected function buildUnique(ColumnInterface $column): string
114
    {
115
        if ($column->isPrimaryKey()) {
116
            return '';
117
        }
118
119
        return $column->isUnique() ? ' UNIQUE' : '';
120
    }
121
122
    /**
123
     * Builds the default value specification for the column.
124
     *
125
     * @return string A string containing the DEFAULT keyword and the default value.
126
     */
127
    protected function buildDefault(ColumnInterface $column): string
128
    {
129
        if ($column->isAutoIncrement()) {
130
            return '';
131
        }
132
133
        $defaultValue = $this->buildDefaultValue($column);
134
135
        if ($defaultValue === null) {
136
            return '';
137
        }
138
139
        return " DEFAULT $defaultValue";
140
    }
141
142
    /**
143
     * Return the default value for the column.
144
     *
145
     * @return string|null string with default value of column.
146
     */
147
    protected function buildDefaultValue(ColumnInterface $column): string|null
148
    {
149
        $value = $column->dbTypecast($column->getDefaultValue());
150
151
        if ($value === null) {
152
            return $column->isAllowNull() === true ? 'NULL' : null;
153
        }
154
155
        if ($value instanceof ExpressionInterface) {
156
            return $this->queryBuilder->buildExpression($value);
157
        }
158
159
        /** @var string */
160
        return match (get_debug_type($value)) {
161
            'int' => (string) $value,
162
            'float' => DbStringHelper::normalizeFloat((string) $value),
163
            'bool' => $value ? 'TRUE' : 'FALSE',
164
            default => $this->quoter->quoteValue((string) $value),
165
        };
166
    }
167
168
    /**
169
     * Builds the check constraint for the column.
170
     *
171
     * @return string A string containing the CHECK constraint.
172
     */
173
    protected function buildCheck(ColumnInterface $column): string
174
    {
175
        $check = $column->getCheck();
176
177
        return !empty($check) ? " CHECK($check)" : '';
178
    }
179
180
    /**
181
     * Builds the unsigned string for column. Default is empty string.
182
     *
183
     * @return string A string containing the UNSIGNED keyword.
184
     */
185
    protected function buildUnsigned(ColumnInterface $column): string
0 ignored issues
show
Unused Code introduced by
The parameter $column is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

185
    protected function buildUnsigned(/** @scrutinizer ignore-unused */ ColumnInterface $column): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
186
    {
187
        return '';
188
    }
189
190
    /**
191
     * Builds the custom string that's appended to column definition.
192
     *
193
     * @return string A string containing the custom SQL fragment appended to column definition.
194
     */
195
    protected function buildExtra(ColumnInterface $column): string
196
    {
197
        $extra = $column->getExtra();
198
199
        return !empty($extra) ? " $extra" : '';
200
    }
201
202
    /**
203
     * Builds the comment clause for the column. Default is empty string.
204
     *
205
     * @return string A string containing the COMMENT keyword and the comment itself.
206
     */
207
    protected function buildComment(ColumnInterface $column): string
0 ignored issues
show
Unused Code introduced by
The parameter $column is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

207
    protected function buildComment(/** @scrutinizer ignore-unused */ ColumnInterface $column): string

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
208
    {
209
        return '';
210
    }
211
212
    private function buildReferences(ColumnInterface $column): string
213
    {
214
        $reference = $this->buildReferenceDefinition($column);
215
216
        if ($reference === null) {
217
            return '';
218
        }
219
220
        return "REFERENCES $reference";
221
    }
222
223
    protected function buildReferenceDefinition(ColumnInterface $column): string|null
224
    {
225
        /** @var ForeignKeyConstraint|null $reference */
226
        $reference = $column->getReference();
227
        $table = $reference?->getForeignTableName();
228
229
        if ($table === null) {
230
            return null;
231
        }
232
233
        if (null !== $schema = $reference->getForeignSchemaName()) {
234
            $sql = $this->quoter->quoteTableName($schema) . '.' . $this->quoter->quoteTableName($table);
235
        } else {
236
            $sql = $this->quoter->quoteTableName($table);
237
        }
238
239
        $columns = $reference->getForeignColumnNames();
240
241
        if (!empty($columns)) {
242
            $sql .= ' (' . $this->queryBuilder->buildColumns($columns) . ')';
243
        }
244
245
        if (null !== $onDelete = $reference->getOnDelete()) {
246
            $sql .= ' ON DELETE ' . $onDelete;
247
        }
248
249
        if (null !== $onUpdate = $reference->getOnUpdate()) {
250
            $sql .= ' ON UPDATE ' . $onUpdate;
251
        }
252
253
        return $sql;
254
    }
255
}
256