Completed
Branch feature/pre-split (b5c37f)
by Anton
03:43
created

MySQLColumn::getDefaultValue()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 4
nc 2
nop 0
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
<?php
2
/**
3
 * components
4
 *
5
 * @author    Wolfy-J
6
 */
7
namespace Spiral\Database\Drivers\MySQL\Schemas;
8
9
use Spiral\Database\Entities\Driver;
10
use Spiral\Database\Injections\Fragment;
11
use Spiral\Database\Schemas\Prototypes\AbstractColumn;
12
13
class MySQLColumn extends AbstractColumn
14
{
15
    /**
16
     * {@inheritdoc}
17
     */
18
    protected $mapping = [
19
        //Primary sequences
20
        'primary'     => [
21
            'type'          => 'int',
22
            'size'          => 11,
23
            'autoIncrement' => true,
24
            'nullable'      => false,
25
        ],
26
        'bigPrimary'  => [
27
            'type'          => 'bigint',
28
            'size'          => 20,
29
            'autoIncrement' => true,
30
            'nullable'      => false,
31
        ],
32
33
        //Enum type (mapped via method)
34
        'enum'        => 'enum',
35
36
        //Logical types
37
        'boolean'     => ['type' => 'tinyint', 'size' => 1],
38
39
        //Integer types (size can always be changed with size method), longInteger has method alias
40
        //bigInteger
41
        'integer'     => ['type' => 'int', 'size' => 11],
42
        'tinyInteger' => ['type' => 'tinyint', 'size' => 4],
43
        'bigInteger'  => ['type' => 'bigint', 'size' => 20],
44
45
        //String with specified length (mapped via method)
46
        'string'      => 'varchar',
47
48
        //Generic types
49
        'text'        => 'text',
50
        'tinyText'    => 'tinytext',
51
        'longText'    => 'longtext',
52
53
        //Real types
54
        'double'      => 'double',
55
        'float'       => 'float',
56
57
        //Decimal type (mapped via method)
58
        'decimal'     => 'decimal',
59
60
        //Date and Time types
61
        'datetime'    => 'datetime',
62
        'date'        => 'date',
63
        'time'        => 'time',
64
        'timestamp'   => [
65
            'type'         => 'timestamp',
66
            'defaultValue' => self::DATETIME_DEFAULT,
67
        ],
68
69
        //Binary types
70
        'binary'      => 'blob',
71
        'tinyBinary'  => 'tinyblob',
72
        'longBinary'  => 'longblob',
73
74
        //Additional types
75
        'json'        => 'text',
76
    ];
77
78
    /**
79
     * {@inheritdoc}
80
     */
81
    protected $reverseMapping = [
82
        'primary'     => [['type' => 'int', 'autoIncrement' => true]],
83
        'bigPrimary'  => ['serial', ['type' => 'bigint', 'autoIncrement' => true]],
84
        'enum'        => ['enum'],
85
        'boolean'     => ['bool', 'boolean', ['type' => 'tinyint', 'size' => 1]],
86
        'integer'     => ['int', 'integer', 'smallint', 'mediumint'],
87
        'tinyInteger' => ['tinyint'],
88
        'bigInteger'  => ['bigint'],
89
        'string'      => ['varchar', 'char'],
90
        'text'        => ['text', 'mediumtext'],
91
        'tinyText'    => ['tinytext'],
92
        'longText'    => ['longtext'],
93
        'double'      => ['double'],
94
        'float'       => ['float', 'real'],
95
        'decimal'     => ['decimal'],
96
        'datetime'    => ['datetime'],
97
        'date'        => ['date'],
98
        'time'        => ['time'],
99
        'timestamp'   => ['timestamp'],
100
        'binary'      => ['blob', 'binary', 'varbinary'],
101
        'tinyBinary'  => ['tinyblob'],
102
        'longBinary'  => ['longblob'],
103
    ];
104
105
    /**
106
     * List of types forbids default value set.
107
     *
108
     * @var array
109
     */
110
    protected $forbiddenDefaults = [
111
        'text',
112
        'mediumtext',
113
        'tinytext',
114
        'longtext',
115
        'blog',
116
        'tinyblob',
117
        'longblob',
118
    ];
119
120
    /**
121
     * Column is auto incremental.
122
     *
123
     * @var bool
124
     */
125
    protected $autoIncrement = false;
126
127
    /**
128
     * {@inheritdoc}
129
     */
130
    public function getDefaultValue()
131
    {
132
        if (in_array($this->type, $this->forbiddenDefaults)) {
133
            return null;
134
        }
135
136
        return parent::getDefaultValue();
137
    }
138
139
    /**
140
     * {@inheritdoc}
141
     */
142
    public function sqlStatement(Driver $driver): string
143
    {
144
        $defaultValue = $this->defaultValue;
145
146
        if (in_array($this->type, $this->forbiddenDefaults)) {
147
            //Flushing default value for forbidden types
148
            $this->defaultValue = null;
149
        }
150
151
        $statement = parent::sqlStatement($driver);
152
153
        $this->defaultValue = $defaultValue;
154
        if ($this->autoIncrement) {
155
            return "{$statement} AUTO_INCREMENT";
156
        }
157
158
        return $statement;
159
    }
160
161
    /**
162
     * @param string $table Table name.
163
     * @param array  $schema
164
     *
165
     * @return MySQLColumn
166
     */
167
    public static function createInstance(string $table, array $schema): self
168
    {
169
        $column = new self($table, $schema['Field']);
170
171
        $column->type = $schema['Type'];
172
        $column->nullable = strtolower($schema['Null']) == 'yes';
173
        $column->defaultValue = $schema['Default'];
174
        $column->autoIncrement = stripos($schema['Extra'], 'auto_increment') !== false;
175
176
        if (!preg_match(
177
            '/^(?P<type>[a-z]+)(?:\((?P<options>[^\)]+)\))?/',
178
            $column->type,
179
            $matches
180
        )
181
        ) {
182
            //No extra definitions
183
            return $column;
184
        }
185
186
        $column->type = $matches['type'];
187
188 View Code Duplication
        if (!empty($matches['options'])) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
189
            $options = explode(',', $matches['options']);
190
191
            if (count($options) > 1) {
192
                $column->precision = (int)$options[0];
193
                $column->scale = (int)$options[1];
194
            } else {
195
                $column->size = (int)$options[0];
196
            }
197
        }
198
199
        //Fetching enum values
200
        if ($column->abstractType() == 'enum' && !empty($options)) {
201
            $column->enumValues = array_map(function ($value) {
202
                return trim($value, $value[0]);
203
            }, $options);
204
205
            return $column;
206
        }
207
208
        //Default value conversions
209
        if ($column->type == 'bit' && $column->hasDefaultValue()) {
210
            //Cutting b\ and '
211
            $column->defaultValue = new Fragment($column->defaultValue);
212
        }
213
214
        if (
215
            $column->abstractType() == 'timestamp'
216
            && $column->defaultValue == '0000-00-00 00:00:00'
217
        ) {
218
            //Normalizing default value; todo: is it needed?
219
            $column->defaultValue = self::DATETIME_DEFAULT;
220
        }
221
222
        return $column;
223
    }
224
225
    /**
226
     * {@inheritdoc}
227
     */
228
    protected function prepareDefault(Driver $driver): string
229
    {
230
        //todo: is it needed?
231
        if ($this->abstractType() == 'timestamp' && is_scalar($this->defaultValue)) {
232
            if (is_numeric($this->defaultValue)) {
233
                //Nothing to do
234
                return (int)$this->defaultValue;
235
            }
236
237
            $datetime = new \DateTime($this->defaultValue, $driver->getTimezone());
238
239
            return $datetime->getTimestamp();
240
        }
241
242
        return parent::prepareDefault($driver);
243
    }
244
}