Completed
Push — master ( f1a1d9...18cd95 )
by Paweł
30s queued 17s
created

ColumnSchemaBuilder::buildDefaultValue()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 23
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 16
CRAP Score 8.0877

Importance

Changes 0
Metric Value
cc 8
eloc 16
nc 8
nop 0
dl 0
loc 23
ccs 16
cts 18
cp 0.8889
crap 8.0877
rs 8.4444
c 0
b 0
f 0
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\db;
9
10
use Yii;
11
use yii\base\BaseObject;
12
use yii\helpers\StringHelper;
13
14
/**
15
 * ColumnSchemaBuilder helps to define database schema types using a PHP interface.
16
 *
17
 * See [[SchemaBuilderTrait]] for more detailed description and usage examples.
18
 *
19
 * @author Vasenin Matvey <[email protected]>
20
 * @since 2.0.6
21
 */
22
class ColumnSchemaBuilder extends BaseObject
23
{
24
    // Internally used constants representing categories that abstract column types fall under.
25
    // See [[$categoryMap]] for mappings of abstract column types to category.
26
    // @since 2.0.8
27
    const CATEGORY_PK = 'pk';
28
    const CATEGORY_STRING = 'string';
29
    const CATEGORY_NUMERIC = 'numeric';
30
    const CATEGORY_TIME = 'time';
31
    const CATEGORY_OTHER = 'other';
32
33
    /**
34
     * @var string the column type definition such as INTEGER, VARCHAR, DATETIME, etc.
35
     */
36
    protected $type;
37
    /**
38
     * @var int|string|array column size or precision definition. This is what goes into the parenthesis after
39
     * the column type. This can be either a string, an integer or an array. If it is an array, the array values will
40
     * be joined into a string separated by comma.
41
     */
42
    protected $length;
43
    /**
44
     * @var bool|null whether the column is or not nullable. If this is `true`, a `NOT NULL` constraint will be added.
45
     * If this is `false`, a `NULL` constraint will be added.
46
     */
47
    protected $isNotNull;
48
    /**
49
     * @var bool whether the column values should be unique. If this is `true`, a `UNIQUE` constraint will be added.
50
     */
51
    protected $isUnique = false;
52
    /**
53
     * @var string the `CHECK` constraint for the column.
54
     */
55
    protected $check;
56
    /**
57
     * @var mixed default value of the column.
58
     */
59
    protected $default;
60
    /**
61
     * @var mixed SQL string to be appended to column schema definition.
62
     * @since 2.0.9
63
     */
64
    protected $append;
65
    /**
66
     * @var bool whether the column values should be unsigned. If this is `true`, an `UNSIGNED` keyword will be added.
67
     * @since 2.0.7
68
     */
69
    protected $isUnsigned = false;
70
    /**
71
     * @var string the column after which this column will be added.
72
     * @since 2.0.8
73
     */
74
    protected $after;
75
    /**
76
     * @var bool whether this column is to be inserted at the beginning of the table.
77
     * @since 2.0.8
78
     */
79
    protected $isFirst;
80
81
82
    /**
83
     * @var array mapping of abstract column types (keys) to type categories (values).
84
     * @since 2.0.8
85
     */
86
    public $categoryMap = [
87
        Schema::TYPE_PK => self::CATEGORY_PK,
88
        Schema::TYPE_UPK => self::CATEGORY_PK,
89
        Schema::TYPE_BIGPK => self::CATEGORY_PK,
90
        Schema::TYPE_UBIGPK => self::CATEGORY_PK,
91
        Schema::TYPE_CHAR => self::CATEGORY_STRING,
92
        Schema::TYPE_STRING => self::CATEGORY_STRING,
93
        Schema::TYPE_TEXT => self::CATEGORY_STRING,
94
        Schema::TYPE_TINYINT => self::CATEGORY_NUMERIC,
95
        Schema::TYPE_SMALLINT => self::CATEGORY_NUMERIC,
96
        Schema::TYPE_INTEGER => self::CATEGORY_NUMERIC,
97
        Schema::TYPE_BIGINT => self::CATEGORY_NUMERIC,
98
        Schema::TYPE_FLOAT => self::CATEGORY_NUMERIC,
99
        Schema::TYPE_DOUBLE => self::CATEGORY_NUMERIC,
100
        Schema::TYPE_DECIMAL => self::CATEGORY_NUMERIC,
101
        Schema::TYPE_DATETIME => self::CATEGORY_TIME,
102
        Schema::TYPE_TIMESTAMP => self::CATEGORY_TIME,
103
        Schema::TYPE_TIME => self::CATEGORY_TIME,
104
        Schema::TYPE_DATE => self::CATEGORY_TIME,
105
        Schema::TYPE_BINARY => self::CATEGORY_OTHER,
106
        Schema::TYPE_BOOLEAN => self::CATEGORY_NUMERIC,
107
        Schema::TYPE_MONEY => self::CATEGORY_NUMERIC,
108
    ];
109
    /**
110
     * @var \yii\db\Connection the current database connection. It is used mainly to escape strings
111
     * safely when building the final column schema string.
112
     * @since 2.0.8
113
     */
114
    public $db;
115
    /**
116
     * @var string comment value of the column.
117
     * @since 2.0.8
118
     */
119
    public $comment;
120
121
    /**
122
     * Create a column schema builder instance giving the type and value precision.
123
     *
124
     * @param string $type type of the column. See [[$type]].
125
     * @param int|string|array $length length or precision of the column. See [[$length]].
126
     * @param \yii\db\Connection $db the current database connection. See [[$db]].
127
     * @param array $config name-value pairs that will be used to initialize the object properties
128
     */
129 56
    public function __construct($type, $length = null, $db = null, $config = [])
130
    {
131 56
        $this->type = $type;
132 56
        $this->length = $length;
133 56
        $this->db = $db;
134 56
        parent::__construct($config);
135 56
    }
136
137
    /**
138
     * Adds a `NOT NULL` constraint to the column.
139
     * @return $this
140
     */
141 37
    public function notNull()
142
    {
143 37
        $this->isNotNull = true;
144 37
        return $this;
145
    }
146
147
    /**
148
     * Adds a `NULL` constraint to the column.
149
     * @return $this
150
     * @since 2.0.9
151
     */
152 5
    public function null()
153
    {
154 5
        $this->isNotNull = false;
155 5
        return $this;
156
    }
157
158
    /**
159
     * Adds a `UNIQUE` constraint to the column.
160
     * @return $this
161
     */
162 1
    public function unique()
163
    {
164 1
        $this->isUnique = true;
165 1
        return $this;
166
    }
167
168
    /**
169
     * Sets a `CHECK` constraint for the column.
170
     * @param string $check the SQL of the `CHECK` constraint to be added.
171
     * @return $this
172
     */
173 5
    public function check($check)
174
    {
175 5
        $this->check = $check;
176 5
        return $this;
177
    }
178
179
    /**
180
     * Specify the default value for the column.
181
     * @param mixed $default the default value.
182
     * @return $this
183
     */
184 6
    public function defaultValue($default)
185
    {
186 6
        if ($default === null) {
187 4
            $this->null();
188
        }
189
190 6
        $this->default = $default;
191 6
        return $this;
192
    }
193
194
    /**
195
     * Specifies the comment for column.
196
     * @param string $comment the comment
197
     * @return $this
198
     * @since 2.0.8
199
     */
200 8
    public function comment($comment)
201
    {
202 8
        $this->comment = $comment;
203 8
        return $this;
204
    }
205
206
    /**
207
     * Marks column as unsigned.
208
     * @return $this
209
     * @since 2.0.7
210
     */
211 10
    public function unsigned()
212
    {
213 10
        switch ($this->type) {
214 10
            case Schema::TYPE_PK:
215 4
                $this->type = Schema::TYPE_UPK;
216 4
                break;
217 10
            case Schema::TYPE_BIGPK:
218 4
                $this->type = Schema::TYPE_UBIGPK;
219 4
                break;
220
        }
221 10
        $this->isUnsigned = true;
222 10
        return $this;
223
    }
224
225
    /**
226
     * Adds an `AFTER` constraint to the column.
227
     * Note: MySQL, Oracle and Cubrid support only.
228
     * @param string $after the column after which $this column will be added.
229
     * @return $this
230
     * @since 2.0.8
231
     */
232 2
    public function after($after)
233
    {
234 2
        $this->after = $after;
235 2
        return $this;
236
    }
237
238
    /**
239
     * Adds an `FIRST` constraint to the column.
240
     * Note: MySQL, Oracle and Cubrid support only.
241
     * @return $this
242
     * @since 2.0.8
243
     */
244 4
    public function first()
245
    {
246 4
        $this->isFirst = true;
247 4
        return $this;
248
    }
249
250
    /**
251
     * Specify the default SQL expression for the column.
252
     * @param string $default the default value expression.
253
     * @return $this
254
     * @since 2.0.7
255
     */
256 1
    public function defaultExpression($default)
257
    {
258 1
        $this->default = new Expression($default);
259 1
        return $this;
260
    }
261
262
    /**
263
     * Specify additional SQL to be appended to column definition.
264
     * Position modifiers will be appended after column definition in databases that support them.
265
     * @param string $sql the SQL string to be appended.
266
     * @return $this
267
     * @since 2.0.9
268
     */
269 5
    public function append($sql)
270
    {
271 5
        $this->append = $sql;
272 5
        return $this;
273
    }
274
275
    /**
276
     * Builds the full string for the column's schema.
277
     * @return string
278
     */
279 18
    public function __toString()
280
    {
281 18
        switch ($this->getTypeCategory()) {
282 18
            case self::CATEGORY_PK:
283 1
                $format = '{type}{check}{comment}{append}';
284 1
                break;
285
            default:
286 18
                $format = '{type}{length}{notnull}{unique}{default}{check}{comment}{append}';
287
        }
288
289 18
        return $this->buildCompleteString($format);
290
    }
291
292
    /**
293
     * Builds the length/precision part of the column.
294
     * @return string
295
     */
296 54
    protected function buildLengthString()
297
    {
298 54
        if ($this->length === null || $this->length === []) {
299 44
            return '';
300
        }
301 13
        if (is_array($this->length)) {
302 2
            $this->length = implode(',', $this->length);
303
        }
304
305 13
        return "({$this->length})";
306
    }
307
308
    /**
309
     * Builds the not null constraint for the column.
310
     * @return string returns 'NOT NULL' if [[isNotNull]] is true,
311
     * 'NULL' if [[isNotNull]] is false or an empty string otherwise.
312
     */
313 54
    protected function buildNotNullString()
314
    {
315 54
        if ($this->isNotNull === true) {
316 35
            return ' NOT NULL';
317 52
        } elseif ($this->isNotNull === false) {
318 3
            return ' NULL';
319
        }
320
321 51
        return '';
322
    }
323
324
    /**
325
     * Builds the unique constraint for the column.
326
     * @return string returns string 'UNIQUE' if [[isUnique]] is true, otherwise it returns an empty string.
327
     */
328 54
    protected function buildUniqueString()
329
    {
330 54
        return $this->isUnique ? ' UNIQUE' : '';
331
    }
332
333
    /**
334
     * Return the default value for the column.
335
     * @return string|null string with default value of column.
336
     */
337 54
    protected function buildDefaultValue()
338
    {
339 54
        if ($this->default === null) {
340 53
            return $this->isNotNull === false ? 'NULL' : null;
341
        }
342
343 4
        switch (gettype($this->default)) {
344 4
            case 'double':
345 4
                // ensure type cast always has . as decimal separator in all locales
346 1
                $defaultValue = StringHelper::floatToString($this->default);
347 1
                break;
348 3
            case 'boolean':
349
                $defaultValue = $this->default ? 'TRUE' : 'FALSE';
350
                break;
351
            case 'integer':
352 3
            case 'object':
353 1
                $defaultValue = (string) $this->default;
354 1
                break;
355 2
            default:
356 2
                $defaultValue = "'{$this->default}'";
357 2
        }
358
359 1
        return $defaultValue;
360
    }
361
362 4
    /**
363
     * Builds the default value specification for the column.
364
     * @return string string with default value of column.
365
     */
366
    protected function buildDefaultString()
367
    {
368
        $defaultValue = $this->buildDefaultValue();
369 54
        if ($defaultValue === null) {
370
            return '';
371 54
        }
372
373
        return ' DEFAULT ' . $defaultValue;
374
    }
375
376
    /**
377
     * Builds the check constraint for the column.
378
     * @return string a string containing the CHECK constraint.
379 18
     */
380
    protected function buildCheckString()
381 18
    {
382
        return $this->check !== null ? " CHECK ({$this->check})" : '';
383
    }
384
385
    /**
386
     * Builds the unsigned string for column. Defaults to unsupported.
387
     * @return string a string containing UNSIGNED keyword.
388
     * @since 2.0.7
389 33
     */
390
    protected function buildUnsignedString()
391 33
    {
392
        return '';
393
    }
394
395
    /**
396
     * Builds the after constraint for the column. Defaults to unsupported.
397
     * @return string a string containing the AFTER constraint.
398
     * @since 2.0.8
399 1
     */
400
    protected function buildAfterString()
401 1
    {
402
        return '';
403
    }
404
405
    /**
406
     * Builds the first constraint for the column. Defaults to unsupported.
407
     * @return string a string containing the FIRST constraint.
408
     * @since 2.0.8
409 54
     */
410
    protected function buildFirstString()
411 54
    {
412
        return '';
413
    }
414
415
    /**
416
     * Builds the custom string that's appended to column definition.
417
     * @return string custom string to append.
418
     * @since 2.0.9
419 54
     */
420
    protected function buildAppendString()
421 54
    {
422
        return $this->append !== null ? ' ' . $this->append : '';
423
    }
424
425
    /**
426
     * Returns the category of the column type.
427
     * @return string a string containing the column type category name.
428
     * @since 2.0.8
429 33
     */
430
    protected function getTypeCategory()
431 33
    {
432
        return isset($this->categoryMap[$this->type]) ? $this->categoryMap[$this->type] : null;
433
    }
434
435
    /**
436
     * Builds the comment specification for the column.
437
     * @return string a string containing the COMMENT keyword and the comment itself
438
     * @since 2.0.8
439
     */
440 54
    protected function buildCommentString()
441
    {
442
        return '';
443 54
    }
444 54
445 54
    /**
446 54
     * Returns the complete column definition from input format.
447 54
     * @param string $format the format of the definition.
448 54
     * @return string a string containing the complete column definition.
449 54
     * @since 2.0.8
450 54
     */
451 54
    protected function buildCompleteString($format)
452 54
    {
453
        $placeholderValues = [
454 54
            '{type}' => $this->type,
455
            '{length}' => $this->buildLengthString(),
456
            '{unsigned}' => $this->buildUnsignedString(),
457
            '{notnull}' => $this->buildNotNullString(),
458
            '{unique}' => $this->buildUniqueString(),
459
            '{default}' => $this->buildDefaultString(),
460
            '{check}' => $this->buildCheckString(),
461
            '{comment}' => $this->buildCommentString(),
462
            '{pos}' => $this->isFirst ? $this->buildFirstString() : $this->buildAfterString(),
463
            '{append}' => $this->buildAppendString(),
464
        ];
465
        return strtr($format, $placeholderValues);
466
    }
467
}
468