Completed
Branch feature/pre-split (76ded7)
by Anton
03:22
created

ColumnSchema::createInstance()   C

Complexity

Conditions 10
Paths 16

Size

Total Lines 57
Code Lines 31

Duplication

Lines 10
Ratio 17.54 %

Importance

Changes 0
Metric Value
cc 10
eloc 31
nc 16
nop 2
dl 10
loc 57
rs 6.7123
c 0
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 ColumnSchema 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
     * Driver specific column initialization.
163
     *
164
     * @param string $table Table name.
165
     * @param array  $schema
166
     *
167
     * @return self
168
     */
169
    public static function createInstance(string $table, array $schema): self
170
    {
171
        $column = new self($table, $schema['Field']);
172
173
        $column->type = $schema['Type'];
174
        $column->nullable = strtolower($schema['Null']) == 'yes';
175
        $column->defaultValue = $schema['Default'];
176
        $column->autoIncrement = stripos($schema['Extra'], 'auto_increment') !== false;
177
178
        if (!preg_match(
179
            '/^(?P<type>[a-z]+)(?:\((?P<options>[^\)]+)\))?/',
180
            $column->type,
181
            $matches
182
        )
183
        ) {
184
            //No extra definitions
185
            return $column;
186
        }
187
188
        $column->type = $matches['type'];
189
190 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...
191
            $options = $matches['options'];
192
193
            if (count($options) > 1) {
194
                $column->precision = (int)$options[0];
195
                $column->scale = (int)$options[1];
196
            } else {
197
                $column->size = (int)$options[0];
198
            }
199
        }
200
201
        //Fetching enum values
202
        if ($column->abstractType() == 'enum' && !empty($options)) {
203
            $column->enumValues = array_map(function ($value) {
204
                return trim($value, $value[0]);
205
            }, explode(',', $options));
206
207
            return $column;
208
        }
209
210
        //Default value conversions
211
        if ($column->type == 'bit' && $column->hasDefaultValue()) {
212
            //Cutting b\ and '
213
            $column->defaultValue = new Fragment($column->defaultValue);
214
        }
215
216
        if (
217
            $column->abstractType() == 'timestamp'
218
            && $column->defaultValue == '0000-00-00 00:00:00'
219
        ) {
220
            //Normalizing default value; todo: is it needed?
221
            $column->defaultValue = self::DATETIME_DEFAULT;
222
        }
223
224
        return $column;
225
    }
226
227
    /**
228
     * {@inheritdoc}
229
     */
230
    protected function prepareDefault(Driver $driver): string
231
    {
232
        //todo: make sure it works correctly
233
        if ($this->abstractType() == 'timestamp' && is_scalar($this->defaultValue)) {
234
            if (is_numeric($this->defaultValue)) {
235
                //Nothing to do
236
                return (int)$this->defaultValue;
237
            }
238
239
            $datetime = new \DateTime($this->defaultValue, $driver->getTimezone());
240
241
            return $datetime->getTimestamp();
242
        }
243
244
        return parent::prepareDefault($driver);
245
    }
246
}