Completed
Branch feature/pre-split (713d19)
by Anton
03:04
created

SQLiteColumn::sqlStatement()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 9
nc 3
nop 1
dl 0
loc 16
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\SQLite\Schemas;
8
9
use Spiral\Database\Entities\Driver;
10
use Spiral\Database\Schemas\Prototypes\AbstractColumn;
11
12
class SQLiteColumn extends AbstractColumn
13
{
14
    /**
15
     * Default timestamp expression (driver specific).
16
     */
17
    const DATETIME_NOW = 'CURRENT_TIMESTAMP';
18
19
    /**
20
     * {@inheritdoc}
21
     */
22
    protected $mapping = [
23
        //Primary sequences
24
        'primary'     => [
25
            'type'       => 'integer',
26
            'primaryKey' => true,
27
            'nullable'   => false,
28
        ],
29
        'bigPrimary'  => [
30
            'type'       => 'integer',
31
            'primaryKey' => true,
32
            'nullable'   => false,
33
        ],
34
35
        //Enum type (mapped via method)
36
        'enum'        => 'enum',
37
38
        //Logical types
39
        'boolean'     => 'boolean',
40
41
        //Integer types (size can always be changed with size method), longInteger has method alias
42
        //bigInteger
43
        'integer'     => 'integer',
44
        'tinyInteger' => 'tinyint',
45
        'bigInteger'  => 'bigint',
46
47
        //String with specified length (mapped via method)
48
        'string'      => 'text',
49
50
        //Generic types
51
        'text'        => 'text',
52
        'tinyText'    => 'text',
53
        'longText'    => 'text',
54
55
        //Real types
56
        'double'      => 'double',
57
        'float'       => 'real',
58
59
        //Decimal type (mapped via method)
60
        'decimal'     => 'numeric',
61
62
        //Date and Time types
63
        'datetime'    => 'datetime',
64
        'date'        => 'date',
65
        'time'        => 'time',
66
        'timestamp'   => 'timestamp',
67
68
        //Binary types
69
        'binary'      => 'blob',
70
        'tinyBinary'  => 'blob',
71
        'longBinary'  => 'blob',
72
73
        //Additional types
74
        'json'        => 'text',
75
    ];
76
77
    /**
78
     * {@inheritdoc}
79
     */
80
    protected $reverseMapping = [
81
        'primary'     => [['type' => 'integer', 'primaryKey' => true]],
82
        'enum'        => ['enum'],
83
        'boolean'     => ['boolean'],
84
        'integer'     => ['int', 'integer', 'smallint', 'mediumint'],
85
        'tinyInteger' => ['tinyint'],
86
        'bigInteger'  => ['bigint'],
87
        'text'        => ['text', 'string'],
88
        'double'      => ['double'],
89
        'float'       => ['real'],
90
        'decimal'     => ['numeric'],
91
        'datetime'    => ['datetime'],
92
        'date'        => ['date'],
93
        'time'        => ['time'],
94
        'timestamp'   => ['timestamp'],
95
        'binary'      => ['blob'],
96
    ];
97
98
    /**
99
     * Indication that column is primary key.
100
     *
101
     * @var bool
102
     */
103
    protected $primaryKey = false;
104
105
    /**
106
     * DBMS specific reverse mapping must map database specific type into limited set of abstract
107
     * types.
108
     *
109
     * @return string
110
     */
111
    public function abstractType(): string
112
    {
113
        if ($this->primaryKey) {
114
            return 'primary';
115
        }
116
117
        return parent::abstractType();
118
    }
119
120
    /**
121
     * {@inheritdoc}
122
     */
123
    public function sqlStatement(Driver $driver): string
124
    {
125
        $statement = parent::sqlStatement($driver);
126
        if ($this->abstractType() != 'enum') {
127
            return $statement;
128
        }
129
130
        $enumValues = [];
131
        foreach ($this->enumValues as $value) {
132
            $enumValues[] = $driver->quote($value);
133
        }
134
135
        $quoted = $driver->identifier($this->name);
136
137
        return "$statement CHECK ({$quoted} IN (" . implode(', ', $enumValues) . '))';
138
    }
139
140
    /**
141
     * {@inheritdoc}
142
     */
143
    protected function quoteEnum(Driver $driver): string
144
    {
145
        return '';
146
    }
147
148
    /**
149
     * @param string $table Table name.
150
     * @param array  $schema
151
     *
152
     * @return SQLiteColumn
153
     */
154
    public static function createInstance(string $table, array $schema): self
155
    {
156
        $column = new self($table, $schema['name']);
157
158
        $column->nullable = !$schema['notnull'];
159
        $column->type = $schema['type'];
160
        $column->primaryKey = (bool)$schema['pk'];
161
162
        /*
163
         * Normalizing default value.
164
         */
165
        $column->defaultValue = $schema['dflt_value'];
166
167
        if (preg_match('/^[\'""].*?[\'"]$/', $column->defaultValue)) {
168
            $column->defaultValue = substr($column->defaultValue, 1, -1);
169
        }
170
171
        if (!preg_match(
172
            '/^(?P<type>[a-z]+) *(?:\((?P<options>[^\)]+)\))?/',
173
            $schema['type'],
174
            $matches
175
        )
176
        ) {
177
            //No type definition included
178
            return $column;
179
        }
180
181
        //Reformatted type value
182
        $column->type = $matches['type'];
183
184
        //Fetching size options
185 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...
186
            $options = explode(',', $matches['options']);
187
188
            if (count($options) > 1) {
189
                $column->precision = (int)$options[0];
190
                $column->scale = (int)$options[1];
191
            } else {
192
                $column->size = (int)$options[0];
193
            }
194
        }
195
196
        if ($column->type == 'enum') {
197
            //Quoted column name
198
            $quoted = $schema['identifier'];
199
200
            foreach ($schema['table'] as $columnSchema) {
201
                //Looking for enum values in column definition code
202
                if (preg_match(
203
                    "/{$quoted} +enum.*?CHECK *\\({$quoted} in \\((.*?)\\)\\)/i",
204
                    trim($columnSchema),
205
                    $matches
206
                )) {
207
                    $enumValues = explode(',', $matches[1]);
208 View Code Duplication
                    foreach ($enumValues as &$value) {
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...
209
                        //Trimming values
210
                        if (preg_match("/^'?(.*?)'?$/", trim($value), $matches)) {
211
                            //In database: 'value'
212
                            $value = $matches[1];
213
                        }
214
215
                        unset($value);
216
                    }
217
218
                    $column->enumValues = $enumValues;
219
                }
220
            }
221
        }
222
223
        return $column;
224
    }
225
}