Passed
Push — 4.x ( b027a1...820cf2 )
by Kit Loong
62:09
created

DatetimeField::getSQLSrvLength()   B

Complexity

Conditions 9
Paths 9

Size

Total Lines 23
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 90

Importance

Changes 0
Metric Value
cc 9
eloc 18
c 0
b 0
f 0
nc 9
nop 2
dl 0
loc 23
ccs 0
cts 0
cp 0
crap 90
rs 8.0555
1
<?php
2
3
namespace KitLoong\MigrationsGenerator\Generators;
4
5
use Doctrine\DBAL\Schema\Column;
6
use Doctrine\DBAL\Types\DateTimeImmutableType;
7
use Doctrine\DBAL\Types\DateTimeType;
8
use Doctrine\DBAL\Types\DateTimeTzImmutableType;
9
use Doctrine\DBAL\Types\DateTimeTzType;
10
use KitLoong\MigrationsGenerator\MigrationMethod\ColumnModifier;
11
use KitLoong\MigrationsGenerator\MigrationMethod\ColumnName;
12
use KitLoong\MigrationsGenerator\MigrationMethod\ColumnType;
13
use KitLoong\MigrationsGenerator\MigrationsGeneratorSetting;
14
use KitLoong\MigrationsGenerator\Repositories\MySQLRepository;
15
use KitLoong\MigrationsGenerator\Repositories\PgSQLRepository;
16
use KitLoong\MigrationsGenerator\Repositories\SQLSrvRepository;
17
use KitLoong\MigrationsGenerator\Support\Regex;
18
use KitLoong\MigrationsGenerator\Types\DBALTypes;
19 63
20
class DatetimeField
21 63
{
22 63
    const SQLSRV_DATETIME_DEFAULT_SCALE = 3;
23
    const SQLSRV_DATETIME_DEFAULT_LENGTH = 8;
24 12
25
    const SQLSRV_DATETIME_TZ_DEFAULT_SCALE = 7;
26 12
    const SQLSRV_DATETIME_TZ_DEFAULT_LENGTH = 10;
27 6
28 3
    private $decorator;
29 3
    private $mySQLRepository;
30 3
    private $pgSQLRepository;
31 3
    private $sqlSrvRepository;
32
    private $regex;
33
34
    public function __construct(
35 9
        Decorator $decorator,
36 3
        MySQLRepository $mySQLRepository,
37 3
        PgSQLRepository $pgSQLRepository,
38
        SQLSrvRepository $sqlSrvRepository,
39
        Regex $regex
40 9
    ) {
41 3
        $this->decorator = $decorator;
42
        $this->mySQLRepository = $mySQLRepository;
43
        $this->pgSQLRepository = $pgSQLRepository;
44 9
        $this->sqlSrvRepository = $sqlSrvRepository;
45 9
        $this->regex = $regex;
46 3
    }
47
48 9
    public function makeField(string $table, array $field, Column $column, bool $useTimestamps): array
49
    {
50 9
        if ($useTimestamps) {
51
            if ($field['field'] === ColumnName::CREATED_AT) {
52
                return [];
53 6
            } elseif ($field['field'] === ColumnName::UPDATED_AT) {
54
                $field['type'] = ColumnType::TIMESTAMPS;
55 6
                $field['field'] = null;
56 3
            }
57
        }
58 3
59 3
        if ($field['field'] === ColumnName::DELETED_AT && !$column->getNotnull()) {
60
            $field['type'] = ColumnType::SOFT_DELETES;
61
            $field['field'] = null;
62
        }
63
64
        if (isset(FieldGenerator::$fieldTypeMap[$field['type']])) {
65
            $field['type'] = FieldGenerator::$fieldTypeMap[$field['type']];
66
        }
67 12
68
        $length = $this->getLength($table, $column);
69
        if ($length !== null && $length > 0) {
70 12
            if ($field['type'] === ColumnType::SOFT_DELETES) {
71 12
                $field['field'] = ColumnName::DELETED_AT;
72 12
            }
73 12
            $field['args'][] = $length;
74
        }
75
76
        if (app(MigrationsGeneratorSetting::class)->getPlatform() === Platform::MYSQL) {
77 12
            if ($column->getType()->getName() === DBALTypes::TIMESTAMP) {
78
                if ($this->mySQLRepository->useOnUpdateCurrentTimestamp($table, $column->getName())) {
79 12
                    $field['decorators'][] = ColumnModifier::USE_CURRENT_ON_UPDATE;
80 6
                }
81 6
            }
82 6
        }
83 3
84
        return $field;
85
    }
86
87 12
    public function makeDefault(Column $column): string
88
    {
89
        if (in_array($column->getDefault(), ['CURRENT_TIMESTAMP'], true)) {
90
            return ColumnModifier::USE_CURRENT;
91
        } elseif ($column->getDefault() === 'now()') { // For PgSQL
92
            return ColumnModifier::USE_CURRENT;
93
        } else {
94
            $default = $this->decorator->columnDefaultToString($column->getDefault());
95
            return $this->decorator->decorate(ColumnModifier::DEFAULT, [$default]);
96
        }
97
    }
98
99
    /**
100
     * @param  Column[]  $columns
101
     * @return bool
102
     */
103
    public function isUseTimestamps(array $columns): bool
104
    {
105
        /** @var Column[] $timestampsColumns */
106
        $timestampsColumns = [];
107
        foreach ($columns as $column) {
108
            if ($column->getName() === ColumnName::CREATED_AT || $column->getName() === ColumnName::UPDATED_AT) {
109
                $timestampsColumns[] = $column;
110
            }
111
        }
112
113
        $useTimestamps = false;
114
115
        if (count($timestampsColumns) === 2) {
116
            $useTimestamps = true;
117
            foreach ($timestampsColumns as $timestamp) {
118
                if ($timestamp->getNotnull() || $timestamp->getDefault() !== null) {
119
                    $useTimestamps = false;
120
                }
121
            }
122
        }
123
        return $useTimestamps;
124
    }
125
126
    private function getLength(string $table, Column $column): ?int
127
    {
128
        switch (app(MigrationsGeneratorSetting::class)->getPlatform()) {
129
            case Platform::POSTGRESQL:
130
                return $this->getPgSQLLength($table, $column);
131
            case Platform::SQLSERVER:
132
                return $this->getSQLSrvLength($table, $column);
133
            default:
134
                return $column->getLength();
135
        }
136
    }
137
138
    /**
139
     * @param  string  $table
140
     * @param  \Doctrine\DBAL\Schema\Column  $column
141
     * @return int|null
142
     */
143
    private function getPgSQLLength(string $table, Column $column): ?int
144
    {
145
        $rawType = ($this->pgSQLRepository->getTypeByColumnName($table, $column->getName()));
146
        $length = $this->regex->getTextBetween($rawType);
0 ignored issues
show
Bug introduced by
It seems like $rawType can also be of type null; however, parameter $text of KitLoong\MigrationsGener...Regex::getTextBetween() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
        $length = $this->regex->getTextBetween(/** @scrutinizer ignore-type */ $rawType);
Loading history...
147
        if ($length !== null) {
148
            return (int) $length;
149
        } else {
150
            return null;
151
        }
152
    }
153
154
    /**
155
     * @param  string  $table
156
     * @param  \Doctrine\DBAL\Schema\Column  $column
157
     * @return int|null
158
     */
159
    private function getSQLSrvLength(string $table, Column $column): ?int
160
    {
161
        $colDef = $this->sqlSrvRepository->getColumnDefinition($table, $column->getName());
162
163
        switch (get_class($column->getType())) {
164
            case DateTimeType::class:
165
            case DateTimeImmutableType::class:
166
                if ($colDef->getScale() === self::SQLSRV_DATETIME_DEFAULT_SCALE &&
167
                    $colDef->getLength() === self::SQLSRV_DATETIME_DEFAULT_LENGTH) {
168
                    return null;
169
                } else {
170
                    return $column->getScale();
171
                }
172
            case DateTimeTzType::class:
173
            case DateTimeTzImmutableType::class:
174
                if ($colDef->getScale() === self::SQLSRV_DATETIME_TZ_DEFAULT_SCALE &&
175
                    $colDef->getLength() === self::SQLSRV_DATETIME_TZ_DEFAULT_LENGTH) {
176
                    return null;
177
                } else {
178
                    return $column->getScale();
179
                }
180
            default:
181
                return $column->getScale();
182
        }
183
    }
184
}
185