Completed
Pull Request — master (#3206)
by Ilya
39:07 queued 24:33
created

Schema   B

Complexity

Total Complexity 50

Size/Duplication

Total Lines 431
Duplicated Lines 0 %

Test Coverage

Coverage 93.13%

Importance

Changes 0
Metric Value
wmc 50
eloc 104
dl 0
loc 431
ccs 122
cts 131
cp 0.9313
rs 8.4
c 0
b 0
f 0

27 Methods

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