Completed
Push — develop ( 8bd504...58ca2f )
by Freddie
09:29
created

Builder   A

Complexity

Total Complexity 26

Size/Duplication

Total Lines 227
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 26
eloc 86
c 1
b 0
f 0
dl 0
loc 227
ccs 88
cts 88
cp 1
rs 10

12 Methods

Rating   Name   Duplication   Size   Complexity  
A getCollateDatabase() 0 9 2
A isSQLSrvPlatform() 0 3 1
A validateLogic() 0 7 2
A getPrettyTable() 0 16 2
A __construct() 0 14 2
A getConstraints() 0 3 1
A createDatabaseWithUse() 0 6 2
A createTable() 0 35 5
A toSql() 0 24 5
A createUser() 0 16 2
A getTable() 0 3 1
A createDatabase() 0 7 1
1
<?php declare(strict_types=1);
2
/*
3
 * This file is part of FlexPHP.
4
 *
5
 * (c) Freddie Gar <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
namespace FlexPHP\Database;
11
12
use Doctrine\DBAL\Platforms\AbstractPlatform;
13
use Doctrine\DBAL\Schema\Schema as DBALSchema;
14
use Doctrine\DBAL\Schema\SchemaConfig as DBALSchemaConfig;
15
use FlexPHP\Database\Exception\DatabaseValidationException;
16
use FlexPHP\Database\Validations\NameDatabaseValidation;
17
use FlexPHP\Schema\SchemaInterface;
18
19
final class Builder
20
{
21
    public const PLATFORM_MYSQL = 'MySQL';
22
23
    public const PLATFORM_SQLSRV = 'SQLSrv';
24
25
    /**
26
     * @var string
27
     */
28
    private $platform;
29
30
    /**
31
     * @var AbstractPlatform
32
     */
33
    private $DBALPlatform;
34
35
    /**
36
     * @var DBALSchema
37
     */
38
    private $DBALSchema;
39
40
    /**
41
     * @var array<int, string>
42
     */
43
    private $databases = [];
44
45
    /**
46
     * @var array<int, string>
47
     */
48
    private $users = [];
49
50
    /**
51
     * @var array<int, string>
52
     */
53
    private $tables = [];
54
55
    /**
56
     * @var array<int, string>
57
     */
58
    private $constraints = [];
59
60
    /**
61
     * @var array<int, string>
62
     */
63
    private $primaryTables = [];
64
65
    /**
66
     * @var array<int, string>
67
     */
68
    private $foreignTables = [];
69
70
    /**
71
     * @var array<string, string>
72
     */
73
    private $platformSupport = [
74
        self::PLATFORM_MYSQL => 'MySQL57',
75
        self::PLATFORM_SQLSRV => 'SQLServer2012',
76
    ];
77
78 15
    public function __construct(string $platform)
79
    {
80 15
        if (empty($this->platformSupport[$platform])) {
81 1
            throw new DatabaseValidationException(\sprintf(
82 1
                'Platform %s not supported, try: %s',
83 1
                $platform,
84 1
                \implode(', ', \array_keys($this->platformSupport))
85
            ));
86
        }
87
88 14
        $fqdnPlatform = \sprintf('\Doctrine\DBAL\Platforms\%sPlatform', $this->platformSupport[$platform]);
89
90 14
        $this->platform = $platform;
91 14
        $this->DBALPlatform = new $fqdnPlatform();
92 14
    }
93
94 7
    public function createDatabase(string $name): void
95
    {
96 7
        (new NameDatabaseValidation($name))->validate();
97
98 6
        $this->databases[] = $this->DBALPlatform->getCreateDatabaseSQL($name)
99 6
            . ' ' . $this->getCollateDatabase()
100 6
            . ';';
101 6
    }
102
103 3
    public function createDatabaseWithUse(string $name): void
104
    {
105 3
        $this->createDatabase($name);
106
107 3
        if ($this->platform === 'MySQL') {
108 2
            $this->databases[] = "USE {$name};";
109
        }
110 3
    }
111
112 6
    public function createUser(
113
        string $name,
114
        string $password,
115
        string $host = '',
116
        array $permissions = [],
117
        string $database = '*',
118
        string $table = '*'
119
    ): void {
120 6
        $user = new User($name, $password, $host);
121 6
        $user->setPlatform($this->platform);
122
123 6
        $this->users[] = $user->toSqlCreate();
124
125 6
        if (\count($permissions)) {
126 3
            $user->setGrants($permissions, $database, $table);
127 3
            $this->users[] = $user->toSqlPrivileges();
128
        }
129 6
    }
130
131 5
    public function createTable(SchemaInterface $schema): void
132
    {
133 5
        $table = new Table($schema);
134
135 5
        $DBALSchemaConfig = new DBALSchemaConfig();
136 5
        $DBALSchemaConfig->setDefaultTableOptions($table->getOptions());
137
138 5
        $this->DBALSchema = new DBALSchema([], [], $DBALSchemaConfig);
139
140 5
        $DBALTable = $this->DBALSchema->createTable($table->getName());
141
142 5
        $this->primaryTables[] = $table->getName();
143
144 5
        foreach ($table->getColumns() as $column) {
145 5
            $DBALTable->addColumn($column->getName(), $column->getType(), $column->getOptions());
146
147 5
            if ($column->isPrimaryKey()) {
148 3
                $DBALTable->setPrimaryKey([$column->getName()]);
149
            }
150
151 5
            if ($column->isForeingKey()) {
152 3
                $fkRel = $schema->fkRelations()[$column->getName()];
153
154 3
                $DBALTable->addForeignKeyConstraint($fkRel['pkTable'], [$fkRel['pkId']], [$fkRel['fkId']]);
155
156 3
                $this->foreignTables[] = $fkRel['pkTable'];
157
            }
158
        }
159
160 5
        $sentences = $this->DBALSchema->toSql($this->DBALPlatform);
161
162 5
        $this->tables[] = $this->getTable($sentences);
163
164 5
        if (\count($sentences) > 1) {
165 4
            $this->constraints[] = $this->getConstraints(\array_slice($sentences, 1));
166
        }
167 5
    }
168
169 13
    public function toSql(): string
170
    {
171 13
        $this->validateLogic();
172
173 12
        $sql = [];
174 12
        $glue = \str_repeat("\n", 2);
175
176 12
        if (\count($this->databases)) {
177 6
            $sql[] = \implode($glue, $this->databases);
178
        }
179
180 12
        if (\count($this->users)) {
181 6
            $sql[] = \implode($glue, $this->users);
182
        }
183
184 12
        if (\count($this->tables)) {
185 4
            $sql[] = \implode($glue, $this->tables);
186
        }
187
188 12
        if (\count($this->constraints)) {
189 3
            $sql[] = \implode($glue, $this->constraints);
190
        }
191
192 12
        return \implode($glue, $sql);
193
    }
194
195 5
    private function getTable(array $sentences): string
196
    {
197 5
        return $this->getPrettyTable($sentences[0]) . ';';
198
    }
199
200 4
    private function getConstraints(array $sentences): string
201
    {
202 4
        return \implode(";\n\n", $sentences) . ';';
203
    }
204
205 6
    private function getCollateDatabase(): string
206
    {
207 6
        $collate = 'CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci';
208
209 6
        if ($this->isSQLSrvPlatform()) {
210 2
            $collate = 'COLLATE latin1_general_100_ci_ai_sc';
211
        }
212
213 6
        return $collate;
214
    }
215
216 6
    private function isSQLSrvPlatform(): bool
217
    {
218 6
        return $this->platform === self::PLATFORM_SQLSRV;
219
    }
220
221 5
    private function getPrettyTable(string $sql): string
222
    {
223 5
        $tag = '<columns>';
224 5
        $regExpColumns = "/\((?$tag.*)\)/";
225 5
        $prettySql = $sql;
226
227 5
        \preg_match($regExpColumns, $sql, $matches);
228
229 5
        if (!empty($matches['columns'])) {
230 5
            $columns = $matches['columns'];
231 5
            $table = \str_replace($columns, "\n    $tag\n", $sql);
232
233 5
            $prettySql = \str_replace($tag, \str_replace(', ', ",\n    ", $columns), $table);
234
        }
235
236 5
        return $prettySql;
237
    }
238
239 13
    private function validateLogic(): void
240
    {
241 13
        $undefinedTables = \array_diff($this->foreignTables, $this->primaryTables);
242
243 13
        if (count($undefinedTables) > 0) {
244 1
            throw new DatabaseValidationException(
245 1
                'Tables in foreign key not found: ' . \implode(', ', $undefinedTables)
246
            );
247
        }
248 12
    }
249
}
250