Passed
Push — master ( 17742c...8cc9ae )
by Alexander
04:17
created

ColumnSchemaBuilder::append()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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