Failed Conditions
Pull Request — develop (#3348)
by Sergei
65:23
created

Schema   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 386
Duplicated Lines 0 %

Test Coverage

Coverage 95.69%

Importance

Changes 0
Metric Value
wmc 53
eloc 111
dl 0
loc 386
ccs 111
cts 116
cp 0.9569
rs 6.96
c 0
b 0
f 0

28 Methods

Rating   Name   Duplication   Size   Complexity  
A __clone() 0 7 3
A toDropSql() 0 6 1
A hasTable() 0 5 1
A getMigrateToSql() 0 6 1
A dropSequence() 0 6 1
A getUnquotedAssetName() 0 7 2
A getTableNames() 0 3 1
A getMigrateFromSql() 0 6 1
A getNamespaces() 0 3 1
A hasSequence() 0 5 1
A getSequences() 0 3 1
A hasExplicitForeignKeyIndexes() 0 3 1
A _addSequence() 0 16 5
A renameTable() 0 9 1
A createSequence() 0 6 1
A getTables() 0 3 1
A getName() 0 6 1
A toSql() 0 6 1
A __construct() 0 22 6
A getSequence() 0 8 2
A getFullQualifiedAssetName() 0 9 2
A _addTable() 0 17 5
A createNamespace() 0 11 2
A getTable() 0 8 2
A visit() 0 16 5
A dropTable() 0 7 1
A createTable() 0 10 2
A hasNamespace() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like Schema often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Schema, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Schema;
6
7
use Doctrine\DBAL\Platforms\AbstractPlatform;
8
use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector;
9
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
10
use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor;
11
use Doctrine\DBAL\Schema\Visitor\Visitor;
12
use function array_keys;
13
use function assert;
14
use function is_string;
15
use function strpos;
16
use function strtolower;
17
18
/**
19
 * Object representation of a database schema.
20
 *
21
 * Different vendors have very inconsistent naming with regard to the concept
22
 * of a "schema". Doctrine understands a schema as the entity that conceptually
23
 * wraps a set of database objects such as tables, sequences, indexes and
24
 * foreign keys that belong to each other into a namespace. A Doctrine Schema
25
 * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more
26
 * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL.
27
 *
28
 * Every asset in the doctrine schema has a name. A name consists of either a
29
 * namespace.local name pair or just a local unqualified name.
30
 *
31
 * The abstraction layer that covers a PostgreSQL schema is the namespace of an
32
 * database object (asset). A schema can have a name, which will be used as
33
 * default namespace for the unqualified database objects that are created in
34
 * the schema.
35
 *
36
 * In the case of MySQL where cross-database queries are allowed this leads to
37
 * databases being "misinterpreted" as namespaces. This is intentional, however
38
 * the CREATE/DROP SQL visitors will just filter this queries and do not
39
 * execute them. Only the queries for the currently connected database are
40
 * executed.
41
 */
42
class Schema extends AbstractAsset
43
{
44
    /**
45
     * The namespaces in this schema.
46
     *
47
     * @var string[]
48
     */
49
    private $namespaces = [];
50
51
    /** @var Table[] */
52
    protected $_tables = [];
53
54
    /** @var Sequence[] */
55
    protected $_sequences = [];
56
57
    /** @var SchemaConfig */
58
    protected $_schemaConfig = false;
59
60
    /**
61 1762
     * @param Table[]    $tables
62
     * @param Sequence[] $sequences
63
     * @param string[]   $namespaces
64
     */
65
    public function __construct(
66
        array $tables = [],
67 1762
        array $sequences = [],
68 1750
        ?SchemaConfig $schemaConfig = null,
69
        array $namespaces = []
70 1762
    ) {
71 1762
        if ($schemaConfig === null) {
72
            $schemaConfig = new SchemaConfig();
73 1762
        }
74 504
        $this->_schemaConfig = $schemaConfig;
75
        $this->_setName($schemaConfig->getName() ?: 'public');
76
77 1762
        foreach ($namespaces as $namespace) {
78 1571
            $this->createNamespace($namespace);
79
        }
80
81 1761
        foreach ($tables as $table) {
82 941
            $this->_addTable($table);
83
        }
84 1760
85
        foreach ($sequences as $sequence) {
86
            $this->_addSequence($sequence);
87
        }
88
    }
89
90
    public function getName() : string
91
    {
92
        $name = parent::getName();
93
        assert(is_string($name));
94
95
        return $name;
96
    }
97
98
    public function hasExplicitForeignKeyIndexes() : bool
99 1747
    {
100
        return $this->_schemaConfig->hasExplicitForeignKeyIndexes();
101 1747
    }
102 1747
103
    /**
104 1747
     * @throws SchemaException
105 745
     */
106
    protected function _addTable(Table $table) : void
107
    {
108 1747
        $namespaceName = $table->getNamespaceName();
109 1747
        $tableName     = $table->getFullQualifiedName($this->getName());
110 1747
111 1064
        if (isset($this->_tables[$tableName])) {
112
            throw SchemaException::tableAlreadyExists($tableName);
113
        }
114 1747
115 1747
        if ($namespaceName !== null
116 1747
            && ! $table->isInDefaultNamespace($this->getName())
117
            && ! $this->hasNamespace($namespaceName)) {
118
            $this->createNamespace($namespaceName);
119
        }
120
121
        $this->_tables[$tableName] = $table;
122
        $table->setSchemaConfig($this->_schemaConfig);
123 1322
    }
124
125 1322
    /**
126 1322
     * @throws SchemaException
127
     */
128 1322
    protected function _addSequence(Sequence $sequence) : void
129 529
    {
130
        $namespaceName = $sequence->getNamespaceName();
131
        $seqName       = $sequence->getFullQualifiedName($this->getName());
132 1322
133 1322
        if (isset($this->_sequences[$seqName])) {
134 1322
            throw SchemaException::sequenceAlreadyExists($seqName);
135 337
        }
136
137
        if ($namespaceName !== null
138 1322
            && ! $sequence->isInDefaultNamespace($this->getName())
139 1322
            && ! $this->hasNamespace($namespaceName)) {
140
            $this->createNamespace($namespaceName);
141
        }
142
143
        $this->_sequences[$seqName] = $sequence;
144
    }
145
146 1551
    /**
147
     * Returns the namespaces of this schema.
148 1551
     *
149
     * @return string[] A list of namespace names.
150
     */
151
    public function getNamespaces() : array
152
    {
153
        return $this->namespaces;
154
    }
155
156 1554
    /**
157
     * Gets all tables of this schema.
158 1554
     *
159
     * @return Table[]
160
     */
161
    public function getTables() : array
162
    {
163
        return $this->_tables;
164
    }
165
166
    /**
167
     * @throws SchemaException
168 1557
     */
169
    public function getTable(string $tableName) : Table
170 1557
    {
171 1557
        $tableName = $this->getFullQualifiedAssetName($tableName);
172 769
        if (! isset($this->_tables[$tableName])) {
173
            throw SchemaException::tableDoesNotExist($tableName);
174
        }
175 1556
176
        return $this->_tables[$tableName];
177
    }
178
179
    private function getFullQualifiedAssetName(string $name) : string
180
    {
181
        $name = $this->getUnquotedAssetName($name);
182
183 1598
        if (strpos($name, '.') === false) {
184
            $name = $this->getName() . '.' . $name;
185 1598
        }
186
187 1598
        return strtolower($name);
188 1594
    }
189
190
    /**
191 1598
     * Returns the unquoted representation of a given asset name.
192
     *
193
     * @param string $assetName Quoted or unquoted representation of an asset name.
194
     */
195
    private function getUnquotedAssetName(string $assetName) : string
196
    {
197
        if ($this->isIdentifierQuoted($assetName)) {
198
            return $this->trimQuotes($assetName);
199
        }
200
201 1603
        return $assetName;
202
    }
203 1603
204 458
    /**
205
     * Does this schema have a namespace with the given name?
206
     */
207 1602
    public function hasNamespace(string $namespaceName) : bool
208
    {
209
        $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
210
211
        return isset($this->namespaces[$namespaceName]);
212
    }
213
214
    /**
215
     * Does this schema have a table with the given name?
216
     */
217 1180
    public function hasTable(string $tableName) : bool
218
    {
219 1180
        $tableName = $this->getFullQualifiedAssetName($tableName);
220
221 1180
        return isset($this->_tables[$tableName]);
222
    }
223
224
    /**
225
     * Gets all table names, prefixed with a schema name, even the default one if present.
226
     *
227
     * @return string[]
228
     */
229
    public function getTableNames() : array
230
    {
231 1584
        return array_keys($this->_tables);
232
    }
233 1584
234
    public function hasSequence(string $sequenceName) : bool
235 1584
    {
236
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
237
238
        return isset($this->_sequences[$sequenceName]);
239
    }
240
241
    /**
242
     * @throws SchemaException
243
     */
244
    public function getSequence(string $sequenceName) : Sequence
245
    {
246
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
247
        if (! $this->hasSequence($sequenceName)) {
248
            throw SchemaException::sequenceDoesNotExist($sequenceName);
249
        }
250
251
        return $this->_sequences[$sequenceName];
252
    }
253 1213
254
    /**
255 1213
     * @return Sequence[]
256
     */
257 1213
    public function getSequences() : array
258
    {
259
        return $this->_sequences;
260
    }
261
262
    /**
263
     * Creates a new namespace.
264
     *
265
     * @param string $namespaceName The name of the namespace to create.
266
     *
267 1137
     * @return $this
268
     *
269 1137
     * @throws SchemaException
270 1137
     */
271 601
    public function createNamespace(string $namespaceName) : self
272
    {
273
        $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
274 1136
275
        if (isset($this->namespaces[$unquotedNamespaceName])) {
276
            throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName);
277
        }
278
279
        $this->namespaces[$unquotedNamespaceName] = $namespaceName;
280 1553
281
        return $this;
282 1553
    }
283
284
    /**
285
     * Creates a new table.
286
     */
287
    public function createTable(string $tableName) : Table
288
    {
289
        $table = new Table($tableName);
290
        $this->_addTable($table);
291
292
        foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) {
293
            $table->addOption($name, $value);
294 1219
        }
295
296 1219
        return $table;
297
    }
298 1219
299 385
    /**
300
     * Renames a table.
301
     *
302 1219
     * @return $this
303
     */
304 1219
    public function renameTable(string $oldTableName, string $newTableName) : self
305
    {
306
        $table = $this->getTable($oldTableName);
307
        $table->_setName($newTableName);
308
309
        $this->dropTable($oldTableName);
310
        $this->_addTable($table);
311
312
        return $this;
313
    }
314 1727
315
    /**
316 1727
     * Drops a table from the schema.
317 1727
     *
318
     * @return $this
319 1727
     */
320
    public function dropTable(string $tableName) : self
321
    {
322
        $tableName = $this->getFullQualifiedAssetName($tableName);
323 1727
        $this->getTable($tableName);
324
        unset($this->_tables[$tableName]);
325
326
        return $this;
327
    }
328
329
    /**
330
     * Creates a new sequence.
331
     */
332
    public function createSequence(string $sequenceName, int $allocationSize = 1, int $initialValue = 1) : Sequence
333
    {
334 721
        $seq = new Sequence($sequenceName, $allocationSize, $initialValue);
335
        $this->_addSequence($seq);
336 721
337 721
        return $seq;
338
    }
339 721
340 721
    /**
341
     * @return $this
342 721
     */
343
    public function dropSequence(string $sequenceName) : self
344
    {
345
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
346
        unset($this->_sequences[$sequenceName]);
347
348
        return $this;
349
    }
350
351
    /**
352 725
     * Returns an array of necessary SQL queries to create the schema on the given platform.
353
     *
354 725
     * @return string[]
355 725
     */
356 725
    public function toSql(AbstractPlatform $platform) : array
357
    {
358 725
        $sqlCollector = new CreateSchemaSqlCollector($platform);
359
        $this->visit($sqlCollector);
360
361
        return $sqlCollector->getQueries();
362
    }
363
364
    /**
365
     * Return an array of necessary SQL queries to drop the schema on the given platform.
366
     *
367
     * @return string[]
368
     */
369
    public function toDropSql(AbstractPlatform $platform) : array
370 1213
    {
371
        $dropSqlCollector = new DropSchemaSqlCollector($platform);
372 1213
        $this->visit($dropSqlCollector);
373 1213
374
        return $dropSqlCollector->getQueries();
375 1213
    }
376
377
    /**
378
     * @return string[]
379
     */
380
    public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform) : array
381
    {
382
        $comparator = new Comparator();
383 553
        $schemaDiff = $comparator->compare($this, $toSchema);
384
385 553
        return $schemaDiff->toSql($platform);
386 553
    }
387
388 553
    /**
389
     * @return string[]
390
     */
391
    public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform) : array
392
    {
393
        $comparator = new Comparator();
394
        $schemaDiff = $comparator->compare($fromSchema, $this);
395
396 1704
        return $schemaDiff->toSql($platform);
397
    }
398 1704
399 1704
    public function visit(Visitor $visitor) : void
400
    {
401 1704
        $visitor->acceptSchema($this);
402
403
        if ($visitor instanceof NamespaceVisitor) {
404
            foreach ($this->namespaces as $namespace) {
405
                $visitor->acceptNamespace($namespace);
406
            }
407
        }
408
409 133
        foreach ($this->_tables as $table) {
410
            $table->visit($visitor);
411 133
        }
412 133
413
        foreach ($this->_sequences as $sequence) {
414 133
            $sequence->visit($visitor);
415
        }
416
    }
417
418
    /**
419
     * Cloning a Schema triggers a deep clone of all related assets.
420 896
     */
421
    public function __clone()
422 896
    {
423 896
        foreach ($this->_tables as $k => $table) {
424
            $this->_tables[$k] = clone $table;
425 896
        }
426
        foreach ($this->_sequences as $k => $sequence) {
427
            $this->_sequences[$k] = clone $sequence;
428
        }
429
    }
430
}
431