Passed
Pull Request — master (#3233)
by Sergey
12:25
created

Schema::getSequence()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 5
CRAP Score 2

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
ccs 5
cts 5
cp 1
rs 10
c 0
b 0
f 0
cc 2
nc 2
nop 1
crap 2
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 View[]  */
54
    protected $_views = [];
55
56
    /** @var SchemaConfig */
57
    protected $_schemaConfig = false;
58
59
    /**
60
     * @param Table[]             $tables
61
     * @param Sequence[]          $sequences
62
     * @param SchemaConfig | null $schemaConfig
63
     * @param string[]            $namespaces
64
     * @param View[]              $views
65
     */
66 1517
    public function __construct(
67
        array $tables = [],
68
        array $sequences = [],
69
        ?SchemaConfig $schemaConfig = null,
70
        array $namespaces = [],
71
        array $views = []
72
    ) {
73 1517
        if ($schemaConfig === null) {
74 1346
            $schemaConfig = new SchemaConfig();
75
        }
76 1517
        $this->_schemaConfig = $schemaConfig;
77 1517
        $this->_setName($schemaConfig->getName() ?: 'public');
78
79 1517
        foreach ($namespaces as $namespace) {
80 8
            $this->createNamespace($namespace);
81
        }
82
83 1517
        foreach ($tables as $table) {
84 361
            $this->_addTable($table);
85
        }
86
87 1498
        foreach ($sequences as $sequence) {
88 85
            $this->_addSequence($sequence);
89
        }
90
91 1479
        foreach ($views as $view) {
92 57
            $this->_addView($view);
93
        }
94 1460
    }
95
96
    /**
97
     * @return bool
98
     */
99
    public function hasExplicitForeignKeyIndexes()
100
    {
101
        return $this->_schemaConfig->hasExplicitForeignKeyIndexes();
102
    }
103
104
    /**
105
     * @return void
106
     *
107
     * @throws SchemaException
108
     */
109 1023
    protected function _addTable(Table $table)
110
    {
111 1023
        $namespaceName = $table->getNamespaceName();
112 1023
        $tableName     = $table->getFullQualifiedName($this->getName());
113
114 1023
        if (isset($this->_tables[$tableName])) {
115 19
            throw SchemaException::tableAlreadyExists($tableName);
116
        }
117
118 1023
        if (! $table->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
119 152
            $this->createNamespace($namespaceName);
120
        }
121
122 1023
        $this->_tables[$tableName] = $table;
123 1023
        $table->setSchemaConfig($this->_schemaConfig);
124 1023
    }
125
126
    /**
127
     * @return void
128
     *
129
     * @throws SchemaException
130
     */
131 332
    protected function _addSequence(Sequence $sequence)
132
    {
133 332
        $namespaceName = $sequence->getNamespaceName();
134 332
        $seqName       = $sequence->getFullQualifiedName($this->getName());
135
136 332
        if (isset($this->_sequences[$seqName])) {
137 19
            throw SchemaException::sequenceAlreadyExists($seqName);
138
        }
139
140 332
        if (! $sequence->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
141 19
            $this->createNamespace($namespaceName);
142
        }
143
144 332
        $this->_sequences[$seqName] = $sequence;
145 332
    }
146
147
    /**
148
     * @return void
149
     *
150
     * @throws SchemaException
151
     */
152 228
    protected function _addView(View $view)
153
    {
154 228
        $namespaceName = $view->getNamespaceName();
155 228
        $viewName      = $view->getFullQualifiedName($this->getName());
156
157 228
        if (isset($this->_views[$viewName])) {
158 19
            throw SchemaException::viewAlreadyExists($viewName);
159
        }
160
161 228
        if (! $view->isInDefaultNamespace($this->getName()) && ! $this->hasNamespace($namespaceName)) {
162
            $this->createNamespace($namespaceName);
163
        }
164
165 228
        $this->_views[$viewName] = $view;
166 228
    }
167
168
    /**
169
     * Returns the namespaces of this schema.
170
     *
171
     * @return string[] A list of namespace names.
172
     */
173 589
    public function getNamespaces()
174
    {
175 589
        return $this->namespaces;
176
    }
177
178
    /**
179
     * Gets all tables of this schema.
180
     *
181
     * @return Table[]
182
     */
183 646
    public function getTables()
184
    {
185 646
        return $this->_tables;
186
    }
187
188
    /**
189
     * @param string $tableName
190
     *
191
     * @return Table
192
     *
193
     * @throws SchemaException
194
     */
195 627
    public function getTable($tableName)
196
    {
197 627
        $tableName = $this->getFullQualifiedAssetName($tableName);
198 627
        if (! isset($this->_tables[$tableName])) {
199 19
            throw SchemaException::tableDoesNotExist($tableName);
200
        }
201
202 608
        return $this->_tables[$tableName];
203
    }
204
205
    /**
206
     * @param string $name
207
     *
208
     * @return string
209
     */
210 1064
    private function getFullQualifiedAssetName($name)
211
    {
212 1064
        $name = $this->getUnquotedAssetName($name);
213
214 1064
        if (strpos($name, '.') === false) {
215 969
            $name = $this->getName() . '.' . $name;
216
        }
217
218 1064
        return strtolower($name);
219
    }
220
221
    /**
222
     * Returns the unquoted representation of a given asset name.
223
     *
224
     * @param string $assetName Quoted or unquoted representation of an asset name.
225
     *
226
     * @return string
227
     */
228 1159
    private function getUnquotedAssetName($assetName)
229
    {
230 1159
        if ($this->isIdentifierQuoted($assetName)) {
231 38
            return $this->trimQuotes($assetName);
232
        }
233
234 1140
        return $assetName;
235
    }
236
237
    /**
238
     * Does this schema have a namespace with the given name?
239
     *
240
     * @param string $namespaceName
241
     *
242
     * @return bool
243
     */
244 196
    public function hasNamespace($namespaceName)
245
    {
246 196
        $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
247
248 196
        return isset($this->namespaces[$namespaceName]);
249
    }
250
251
    /**
252
     * Does this schema have a table with the given name?
253
     *
254
     * @param string $tableName
255
     *
256
     * @return bool
257
     */
258 608
    public function hasTable($tableName)
259
    {
260 608
        $tableName = $this->getFullQualifiedAssetName($tableName);
261
262 608
        return isset($this->_tables[$tableName]);
263
    }
264
265
    /**
266
     * Gets all table names, prefixed with a schema name, even the default one if present.
267
     *
268
     * @return string[]
269
     */
270
    public function getTableNames()
271
    {
272
        return array_keys($this->_tables);
273
    }
274
275
    /**
276
     * @param string $sequenceName
277
     *
278
     * @return bool
279
     */
280 247
    public function hasSequence($sequenceName)
281
    {
282 247
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
283
284 247
        return isset($this->_sequences[$sequenceName]);
285
    }
286
287
    /**
288
     * @param string $sequenceName
289
     *
290
     * @return Sequence
291
     *
292
     * @throws SchemaException
293
     */
294 171
    public function getSequence($sequenceName)
295
    {
296 171
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
297 171
        if (! $this->hasSequence($sequenceName)) {
298 19
            throw SchemaException::sequenceDoesNotExist($sequenceName);
299
        }
300
301 152
        return $this->_sequences[$sequenceName];
302
    }
303
304
    /**
305
     * @return Sequence[]
306
     */
307 627
    public function getSequences()
308
    {
309 627
        return $this->_sequences;
310
    }
311
312
    /**
313
     * Creates a new namespace.
314
     *
315
     * @param string $namespaceName The name of the namespace to create.
316
     *
317
     * @return \Doctrine\DBAL\Schema\Schema This schema instance.
318
     *
319
     * @throws SchemaException
320
     */
321 217
    public function createNamespace($namespaceName)
322
    {
323 217
        $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
324
325 217
        if (isset($this->namespaces[$unquotedNamespaceName])) {
326 19
            throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName);
327
        }
328
329 217
        $this->namespaces[$unquotedNamespaceName] = $namespaceName;
330
331 217
        return $this;
332
    }
333
334
    /**
335
     * Creates a new table.
336
     *
337
     * @param string $tableName
338
     *
339
     * @return Table
340
     */
341 700
    public function createTable($tableName)
342
    {
343 700
        $table = new Table($tableName);
344 700
        $this->_addTable($table);
345
346 700
        foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) {
347
            $table->addOption($name, $value);
348
        }
349
350 700
        return $table;
351
    }
352
353
    /**
354
     * Renames a table.
355
     *
356
     * @param string $oldTableName
357
     * @param string $newTableName
358
     *
359
     * @return \Doctrine\DBAL\Schema\Schema
360
     */
361 19
    public function renameTable($oldTableName, $newTableName)
362
    {
363 19
        $table = $this->getTable($oldTableName);
364 19
        $table->_setName($newTableName);
365
366 19
        $this->dropTable($oldTableName);
367 19
        $this->_addTable($table);
368
369 19
        return $this;
370
    }
371
372
    /**
373
     * Drops a table from the schema.
374
     *
375
     * @param string $tableName
376
     *
377
     * @return \Doctrine\DBAL\Schema\Schema
378
     */
379 95
    public function dropTable($tableName)
380
    {
381 95
        $tableName = $this->getFullQualifiedAssetName($tableName);
382 95
        $this->getTable($tableName);
383 95
        unset($this->_tables[$tableName]);
384
385 95
        return $this;
386
    }
387
388
    /**
389
     * Creates a new sequence.
390
     *
391
     * @param string $sequenceName
392
     * @param int    $allocationSize
393
     * @param int    $initialValue
394
     *
395
     * @return Sequence
396
     */
397 247
    public function createSequence($sequenceName, $allocationSize = 1, $initialValue = 1)
398
    {
399 247
        $seq = new Sequence($sequenceName, $allocationSize, $initialValue);
400 247
        $this->_addSequence($seq);
401
402 247
        return $seq;
403
    }
404
405
    /**
406
     * @param string $sequenceName
407
     *
408
     * @return \Doctrine\DBAL\Schema\Schema
409
     */
410 19
    public function dropSequence($sequenceName)
411
    {
412 19
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
413 19
        unset($this->_sequences[$sequenceName]);
414
415 19
        return $this;
416
    }
417
418
    /**
419
     * @param string $viewName
420
     *
421
     * @return bool
422
     */
423 171
    public function hasView($viewName)
424
    {
425 171
        $viewName = $this->getFullQualifiedAssetName($viewName);
426
427 171
        return isset($this->_views[$viewName]);
428
    }
429
430
    /**
431
     * @throws SchemaException
432
     */
433 133
    public function getView(string $viewName) : View
434
    {
435 133
        $viewName = $this->getFullQualifiedAssetName($viewName);
436 133
        if (! isset($this->_views[$viewName])) {
437 19
            throw SchemaException::viewDoesNotExist($viewName);
438
        }
439
440 114
        return $this->_views[$viewName];
441
    }
442
443
    /**
444
     * @return View[]
445
     */
446 608
    public function getViews() : array
447
    {
448 608
        return $this->_views;
449
    }
450
451 171
    public function createView(string $viewName, string $sql) : View
452
    {
453 171
        $view = new View($viewName, $sql);
454 171
        $this->_addView($view);
455
456 171
        return $view;
457
    }
458
459 19
    public function dropView(string $viewName) : self
460
    {
461 19
        $viewName = $this->getFullQualifiedAssetName($viewName);
462 19
        unset($this->_views[$viewName]);
463
464 19
        return $this;
465
    }
466
467
    /**
468
     * Returns an array of necessary SQL queries to create the schema on the given platform.
469
     *
470
     * @return string[]
471
     */
472 219
    public function toSql(AbstractPlatform $platform)
473
    {
474 219
        $sqlCollector = new CreateSchemaSqlCollector($platform);
475 219
        $this->visit($sqlCollector);
476
477 219
        return $sqlCollector->getQueries();
478
    }
479
480
    /**
481
     * Return an array of necessary SQL queries to drop the schema on the given platform.
482
     *
483
     * @return string[]
484
     */
485 19
    public function toDropSql(AbstractPlatform $platform)
486
    {
487 19
        $dropSqlCollector = new DropSchemaSqlCollector($platform);
488 19
        $this->visit($dropSqlCollector);
489
490 19
        return $dropSqlCollector->getQueries();
491
    }
492
493
    /**
494
     * @return string[]
495
     */
496
    public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform)
497
    {
498
        $comparator = new Comparator();
499
        $schemaDiff = $comparator->compare($this, $toSchema);
500
501
        return $schemaDiff->toSql($platform);
502
    }
503
504
    /**
505
     * @return string[]
506
     */
507
    public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform)
508
    {
509
        $comparator = new Comparator();
510
        $schemaDiff = $comparator->compare($fromSchema, $this);
511
512
        return $schemaDiff->toSql($platform);
513
    }
514
515
    /**
516
     * @return void
517
     */
518 333
    public function visit(Visitor $visitor)
519
    {
520 333
        $visitor->acceptSchema($this);
521
522 333
        if ($visitor instanceof NamespaceVisitor) {
523 276
            foreach ($this->namespaces as $namespace) {
524 76
                $visitor->acceptNamespace($namespace);
525
            }
526
        }
527
528 333
        foreach ($this->_tables as $table) {
529 333
            $table->visit($visitor);
530
        }
531
532 333
        foreach ($this->_sequences as $sequence) {
533 76
            $sequence->visit($visitor);
534
        }
535
536 333
        foreach ($this->_views as $view) {
537 19
            $view->visit($visitor);
538
        }
539 333
    }
540
541
    /**
542
     * Cloning a Schema triggers a deep clone of all related assets.
543
     *
544
     * @return void
545
     */
546 38
    public function __clone()
547
    {
548 38
        foreach ($this->_tables as $k => $table) {
549 19
            $this->_tables[$k] = clone $table;
550
        }
551 38
        foreach ($this->_sequences as $k => $sequence) {
552 38
            $this->_sequences[$k] = clone $sequence;
553
        }
554 38
        foreach ($this->_views as $k => $view) {
555 19
            $this->_views[$k] = clone $view;
556
        }
557 38
    }
558
}
559