Failed Conditions
Pull Request — master (#3233)
by Sergey
10:50
created

Schema   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 567
Duplicated Lines 0 %

Test Coverage

Coverage 91.52%

Importance

Changes 0
Metric Value
wmc 64
eloc 132
dl 0
loc 567
rs 3.28
c 0
b 0
f 0
ccs 151
cts 165
cp 0.9152

33 Methods

Rating   Name   Duplication   Size   Complexity  
A __clone() 0 10 4
A toDropSql() 0 6 1
A hasTable() 0 5 1
A getMigrateToSql() 0 6 1
A dropSequence() 0 6 1
A hasView() 0 5 1
A getUnquotedAssetName() 0 7 2
A getNamespaces() 0 3 1
A getMigrateFromSql() 0 6 1
A getTableNames() 0 3 1
A _addView() 0 14 4
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 getViews() 0 3 1
A getTables() 0 3 1
A toSql() 0 6 1
B __construct() 0 27 7
A dropView() 0 8 2
A createView() 0 6 1
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 20 6
A dropTable() 0 7 1
A createTable() 0 10 2
A getView() 0 8 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
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\DBAL\Schema;
21
22
use Doctrine\DBAL\Schema\Visitor\CreateSchemaSqlCollector;
23
use Doctrine\DBAL\Schema\Visitor\DropSchemaSqlCollector;
24
use Doctrine\DBAL\Schema\Visitor\NamespaceVisitor;
25
use Doctrine\DBAL\Schema\Visitor\Visitor;
26
use Doctrine\DBAL\Platforms\AbstractPlatform;
27
use function array_keys;
28
use function strpos;
29
use function strtolower;
30
31
/**
32
 * Object representation of a database schema.
33
 *
34
 * Different vendors have very inconsistent naming with regard to the concept
35
 * of a "schema". Doctrine understands a schema as the entity that conceptually
36
 * wraps a set of database objects such as tables, sequences, indexes and
37
 * foreign keys that belong to each other into a namespace. A Doctrine Schema
38
 * has nothing to do with the "SCHEMA" defined as in PostgreSQL, it is more
39
 * related to the concept of "DATABASE" that exists in MySQL and PostgreSQL.
40
 *
41
 * Every asset in the doctrine schema has a name. A name consists of either a
42
 * namespace.local name pair or just a local unqualified name.
43
 *
44
 * The abstraction layer that covers a PostgreSQL schema is the namespace of an
45
 * database object (asset). A schema can have a name, which will be used as
46
 * default namespace for the unqualified database objects that are created in
47
 * the schema.
48
 *
49
 * In the case of MySQL where cross-database queries are allowed this leads to
50
 * databases being "misinterpreted" as namespaces. This is intentional, however
51
 * the CREATE/DROP SQL visitors will just filter this queries and do not
52
 * execute them. Only the queries for the currently connected database are
53
 * executed.
54
 *
55
 * @link   www.doctrine-project.org
56
 * @since  2.0
57
 * @author Benjamin Eberlei <[email protected]>
58
 */
59
class Schema extends AbstractAsset
60
{
61
    /**
62
     * The namespaces in this schema.
63
     *
64
     * @var array
65
     */
66
    private $namespaces = [];
67
68
    /**
69
     * @var \Doctrine\DBAL\Schema\Table[]
70
     */
71
    protected $_tables = [];
72
73
    /**
74
     * @var \Doctrine\DBAL\Schema\Sequence[]
75
     */
76
    protected $_sequences = [];
77
78
    /**
79
     * @var \Doctrine\DBAL\Schema\View[]
80
     */
81
    protected $_views = [];
82
83
    /**
84
     * @var SchemaConfig
85
     */
86
    protected $_schemaConfig = false;
87
88
    /**
89
     * @param \Doctrine\DBAL\Schema\Table[]      $tables
90
     * @param \Doctrine\DBAL\Schema\Sequence[]   $sequences
91
     * @param \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig
92
     * @param array                              $namespaces
93
     * @param \Doctrine\DBAL\Schema\View[]       $views
94
     */
95 1577
    public function __construct(
96
        array $tables = [],
97
        array $sequences = [],
98
        SchemaConfig $schemaConfig = null,
99
        array $namespaces = [],
100
        array $views = []
101
    ) {
102 1577
        if ($schemaConfig == null) {
103 1397
            $schemaConfig = new SchemaConfig();
104
        }
105 1577
        $this->_schemaConfig = $schemaConfig;
106 1577
        $this->_setName($schemaConfig->getName() ?: 'public');
107
108 1577
        foreach ($namespaces as $namespace) {
109 9
            $this->createNamespace($namespace);
110
        }
111
112 1577
        foreach ($tables as $table) {
113 380
            $this->_addTable($table);
114
        }
115
116 1557
        foreach ($sequences as $sequence) {
117 90
            $this->_addSequence($sequence);
118
        }
119
120 1537
        foreach ($views as $view) {
121 60
            $this->_addView($view);
122
        }
123 1517
    }
124
125
    /**
126
     * @return bool
127
     */
128
    public function hasExplicitForeignKeyIndexes()
129
    {
130
        return $this->_schemaConfig->hasExplicitForeignKeyIndexes();
131
    }
132
133
    /**
134
     * @param \Doctrine\DBAL\Schema\Table $table
135
     *
136
     * @return void
137
     *
138
     * @throws \Doctrine\DBAL\Schema\SchemaException
139
     */
140 1077
    protected function _addTable(Table $table)
141
    {
142 1077
        $namespaceName = $table->getNamespaceName();
143 1077
        $tableName = $table->getFullQualifiedName($this->getName());
144
145 1077
        if (isset($this->_tables[$tableName])) {
146 20
            throw SchemaException::tableAlreadyExists($tableName);
147
        }
148
149 1077
        if ( ! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
150 160
            $this->createNamespace($namespaceName);
151
        }
152
153 1077
        $this->_tables[$tableName] = $table;
154 1077
        $table->setSchemaConfig($this->_schemaConfig);
155 1077
    }
156
157
    /**
158
     * @param \Doctrine\DBAL\Schema\Sequence $sequence
159
     *
160
     * @return void
161
     *
162
     * @throws \Doctrine\DBAL\Schema\SchemaException
163
     */
164 350
    protected function _addSequence(Sequence $sequence)
165
    {
166 350
        $namespaceName = $sequence->getNamespaceName();
167 350
        $seqName = $sequence->getFullQualifiedName($this->getName());
168
169 350
        if (isset($this->_sequences[$seqName])) {
170 20
            throw SchemaException::sequenceAlreadyExists($seqName);
171
        }
172
173 350
        if ( ! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
174 20
            $this->createNamespace($namespaceName);
175
        }
176
177 350
        $this->_sequences[$seqName] = $sequence;
178 350
    }
179
180
    /**
181
     * @param \Doctrine\DBAL\Schema\View $view
182
     *
183
     * @return void
184
     *
185
     * @throws \Doctrine\DBAL\Schema\SchemaException
186
     */
187 220
    protected function _addView(View $view)
188
    {
189 220
        $namespaceName = $view->getNamespaceName();
190 220
        $viewName = $view->getFullQualifiedName($this->getName());
191
192 220
        if (isset($this->_views[$viewName])) {
193 20
            throw SchemaException::viewAlreadyExists($viewName);
194
        }
195
196 220
        if ( ! $view->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
197
            $this->createNamespace($namespaceName);
198
        }
199
200 220
        $this->_views[$viewName] = $view;
201 220
    }
202
203
    /**
204
     * Returns the namespaces of this schema.
205
     *
206
     * @return array A list of namespace names.
207
     */
208 600
    public function getNamespaces()
209
    {
210 600
        return $this->namespaces;
211
    }
212
213
    /**
214
     * Gets all tables of this schema.
215
     *
216
     * @return \Doctrine\DBAL\Schema\Table[]
217
     */
218 660
    public function getTables()
219
    {
220 660
        return $this->_tables;
221
    }
222
223
    /**
224
     * @param string $tableName
225
     *
226
     * @return \Doctrine\DBAL\Schema\Table
227
     *
228
     * @throws \Doctrine\DBAL\Schema\SchemaException
229
     */
230 660
    public function getTable($tableName)
231
    {
232 660
        $tableName = $this->getFullQualifiedAssetName($tableName);
233 660
        if (!isset($this->_tables[$tableName])) {
234 20
            throw SchemaException::tableDoesNotExist($tableName);
235
        }
236
237 640
        return $this->_tables[$tableName];
238
    }
239
240
    /**
241
     * @param string $name
242
     *
243
     * @return string
244
     */
245 1100
    private function getFullQualifiedAssetName($name)
246
    {
247 1100
        $name = $this->getUnquotedAssetName($name);
248
249 1100
        if (strpos($name, ".") === false) {
250 1000
            $name = $this->getName() . "." . $name;
251
        }
252
253 1100
        return strtolower($name);
254
    }
255
256
    /**
257
     * Returns the unquoted representation of a given asset name.
258
     *
259
     * @param string $assetName Quoted or unquoted representation of an asset name.
260
     *
261
     * @return string
262
     */
263 1200
    private function getUnquotedAssetName($assetName)
264
    {
265 1200
        if ($this->isIdentifierQuoted($assetName)) {
266 40
            return $this->trimQuotes($assetName);
267
        }
268
269 1180
        return $assetName;
270
    }
271
272
    /**
273
     * Does this schema have a namespace with the given name?
274
     *
275
     * @param string $namespaceName
276
     *
277
     * @return bool
278
     */
279 207
    public function hasNamespace($namespaceName)
280
    {
281 207
        $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
282
283 207
        return isset($this->namespaces[$namespaceName]);
284
    }
285
286
    /**
287
     * Does this schema have a table with the given name?
288
     *
289
     * @param string $tableName
290
     *
291
     * @return bool
292
     */
293 640
    public function hasTable($tableName)
294
    {
295 640
        $tableName = $this->getFullQualifiedAssetName($tableName);
296
297 640
        return isset($this->_tables[$tableName]);
298
    }
299
300
    /**
301
     * Gets all table names, prefixed with a schema name, even the default one if present.
302
     *
303
     * @return array
304
     */
305
    public function getTableNames()
306
    {
307
        return array_keys($this->_tables);
308
    }
309
310
    /**
311
     * @param string $sequenceName
312
     *
313
     * @return bool
314
     */
315 260
    public function hasSequence($sequenceName)
316
    {
317 260
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
318
319 260
        return isset($this->_sequences[$sequenceName]);
320
    }
321
322
    /**
323
     * @param string $sequenceName
324
     *
325
     * @return \Doctrine\DBAL\Schema\Sequence
326
     *
327
     * @throws \Doctrine\DBAL\Schema\SchemaException
328
     */
329 180
    public function getSequence($sequenceName)
330
    {
331 180
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
332 180
        if (!$this->hasSequence($sequenceName)) {
333 20
            throw SchemaException::sequenceDoesNotExist($sequenceName);
334
        }
335
336 160
        return $this->_sequences[$sequenceName];
337
    }
338
339
    /**
340
     * @return \Doctrine\DBAL\Schema\Sequence[]
341
     */
342 640
    public function getSequences()
343
    {
344 640
        return $this->_sequences;
345
    }
346
347
    /**
348
     * Creates a new namespace.
349
     *
350
     * @param string $namespaceName The name of the namespace to create.
351
     *
352
     * @return \Doctrine\DBAL\Schema\Schema This schema instance.
353
     *
354
     * @throws SchemaException
355
     */
356 229
    public function createNamespace($namespaceName)
357
    {
358 229
        $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
359
360 229
        if (isset($this->namespaces[$unquotedNamespaceName])) {
361 20
            throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName);
362
        }
363
364 229
        $this->namespaces[$unquotedNamespaceName] = $namespaceName;
365
366 229
        return $this;
367
    }
368
369
    /**
370
     * Creates a new table.
371
     *
372
     * @param string $tableName
373
     *
374
     * @return \Doctrine\DBAL\Schema\Table
375
     */
376 737
    public function createTable($tableName)
377
    {
378 737
        $table = new Table($tableName);
379 737
        $this->_addTable($table);
380
381 737
        foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) {
382
            $table->addOption($name, $value);
383
        }
384
385 737
        return $table;
386
    }
387
388
    /**
389
     * Renames a table.
390
     *
391
     * @param string $oldTableName
392
     * @param string $newTableName
393
     *
394
     * @return \Doctrine\DBAL\Schema\Schema
395
     */
396 20
    public function renameTable($oldTableName, $newTableName)
397
    {
398 20
        $table = $this->getTable($oldTableName);
399 20
        $table->_setName($newTableName);
400
401 20
        $this->dropTable($oldTableName);
402 20
        $this->_addTable($table);
403
404 20
        return $this;
405
    }
406
407
    /**
408
     * Drops a table from the schema.
409
     *
410
     * @param string $tableName
411
     *
412
     * @return \Doctrine\DBAL\Schema\Schema
413
     */
414 100
    public function dropTable($tableName)
415
    {
416 100
        $tableName = $this->getFullQualifiedAssetName($tableName);
417 100
        $this->getTable($tableName);
418 100
        unset($this->_tables[$tableName]);
419
420 100
        return $this;
421
    }
422
423
    /**
424
     * Creates a new sequence.
425
     *
426
     * @param string $sequenceName
427
     * @param int    $allocationSize
428
     * @param int    $initialValue
429
     *
430
     * @return \Doctrine\DBAL\Schema\Sequence
431
     */
432 260
    public function createSequence($sequenceName, $allocationSize=1, $initialValue=1)
433
    {
434 260
        $seq = new Sequence($sequenceName, $allocationSize, $initialValue);
435 260
        $this->_addSequence($seq);
436
437 260
        return $seq;
438
    }
439
440
    /**
441
     * @param string $sequenceName
442
     *
443
     * @return \Doctrine\DBAL\Schema\Schema
444
     */
445 20
    public function dropSequence($sequenceName)
446
    {
447 20
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
448 20
        unset($this->_sequences[$sequenceName]);
449
450 20
        return $this;
451
    }
452
453
    /**
454
     * @param string $viewName
455
     *
456
     * @return boolean
457
     */
458 160
    public function hasView($viewName)
459
    {
460 160
        $viewName = $this->getFullQualifiedAssetName($viewName);
461
462 160
        return isset($this->_views[$viewName]);
463
    }
464
465
    /**
466
     * @param string $viewName
467
     *
468
     * @return \Doctrine\DBAL\Schema\View
469
     *
470
     * @throws \Doctrine\DBAL\Schema\SchemaException
471
     */
472 120
    public function getView($viewName)
473
    {
474 120
        $viewName = $this->getFullQualifiedAssetName($viewName);
475 120
        if (!isset($this->_views[$viewName])) {
476 20
            throw SchemaException::viewDoesNotExist($viewName);
477
        }
478
479 100
        return $this->_views[$viewName];
480
    }
481
482
    /**
483
     * Gets all views of this schema.
484
     *
485
     * @return \Doctrine\DBAL\Schema\View[]
486
     */
487 620
    public function getViews()
488
    {
489 620
        return $this->_views;
490
    }
491
492
    /**
493
     * Creates a new view.
494
     *
495
     * @param string $viewName
496
     * @param string $sql
497
     *
498
     * @return \Doctrine\DBAL\Schema\View
499
     */
500 160
    public function createView($viewName, $sql)
501
    {
502 160
        $view = new View($viewName, $sql);
503 160
        $this->_addView($view);
504
505 160
        return $view;
506
    }
507
508
    /**
509
     * Drops a view from the schema.
510
     *
511
     * @param string $viewName
512
     *
513
     * @return \Doctrine\DBAL\Schema\Schema
514
     */
515 20
    public function dropView($viewName)
516
    {
517 20
        $viewName = $this->getFullQualifiedAssetName($viewName);
518 20
        if($this->hasView($viewName)){
519 20
          unset($this->_views[$viewName]);
520
        }
521
522 20
        return $this;
523
    }
524
525
    /**
526
     * Returns an array of necessary SQL queries to create the schema on the given platform.
527
     *
528
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
529
     *
530
     * @return array
531
     */
532 231
    public function toSql(AbstractPlatform $platform)
533
    {
534 231
        $sqlCollector = new CreateSchemaSqlCollector($platform);
535 231
        $this->visit($sqlCollector);
536
537 231
        return $sqlCollector->getQueries();
538
    }
539
540
    /**
541
     * Return an array of necessary SQL queries to drop the schema on the given platform.
542
     *
543
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
544
     *
545
     * @return array
546
     */
547 20
    public function toDropSql(AbstractPlatform $platform)
548
    {
549 20
        $dropSqlCollector = new DropSchemaSqlCollector($platform);
550 20
        $this->visit($dropSqlCollector);
551
552 20
        return $dropSqlCollector->getQueries();
553
    }
554
555
    /**
556
     * @param \Doctrine\DBAL\Schema\Schema              $toSchema
557
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
558
     *
559
     * @return array
560
     */
561
    public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform)
562
    {
563
        $comparator = new Comparator();
564
        $schemaDiff = $comparator->compare($this, $toSchema);
565
566
        return $schemaDiff->toSql($platform);
567
    }
568
569
    /**
570
     * @param \Doctrine\DBAL\Schema\Schema              $fromSchema
571
     * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform
572
     *
573
     * @return array
574
     */
575
    public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform)
576
    {
577
        $comparator = new Comparator();
578
        $schemaDiff = $comparator->compare($fromSchema, $this);
579
580
        return $schemaDiff->toSql($platform);
581
    }
582
583
    /**
584
     * @param \Doctrine\DBAL\Schema\Visitor\Visitor $visitor
585
     *
586
     * @return void
587
     */
588 351
    public function visit(Visitor $visitor)
589
    {
590 351
        $visitor->acceptSchema($this);
591
592 351
        if ($visitor instanceof NamespaceVisitor) {
593 291
            foreach ($this->namespaces as $namespace) {
594 80
                $visitor->acceptNamespace($namespace);
595
            }
596
        }
597
598 351
        foreach ($this->_tables as $table) {
599 351
            $table->visit($visitor);
600
        }
601
602 351
        foreach ($this->_sequences as $sequence) {
603 80
            $sequence->visit($visitor);
604
        }
605
606 351
        foreach ($this->_views as $view) {
607 20
            $view->visit($visitor);
608
        }
609 351
    }
610
611
    /**
612
     * Cloning a Schema triggers a deep clone of all related assets.
613
     *
614
     * @return void
615
     */
616 40
    public function __clone()
617
    {
618 40
        foreach ($this->_tables as $k => $table) {
619 20
            $this->_tables[$k] = clone $table;
620
        }
621 40
        foreach ($this->_sequences as $k => $sequence) {
622 40
            $this->_sequences[$k] = clone $sequence;
623
        }
624 40
        foreach ($this->_views as $k => $view) {
625 20
            $this->_views[$k] = clone $view;
626
        }
627 40
    }
628
}
629