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

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