Completed
Pull Request — master (#3610)
by Sergei
06:29
created

Schema::getTableNames()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 2
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
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 SchemaConfig */
54
    protected $_schemaConfig = false;
55
56
    /**
57
     * @param Table[]    $tables
58
     * @param Sequence[] $sequences
59
     * @param string[]   $namespaces
60
     */
61 1886
    public function __construct(
62
        array $tables = [],
63
        array $sequences = [],
64
        ?SchemaConfig $schemaConfig = null,
65
        array $namespaces = []
66
    ) {
67 1886
        if ($schemaConfig === null) {
68 1865
            $schemaConfig = new SchemaConfig();
69
        }
70 1886
        $this->_schemaConfig = $schemaConfig;
71 1886
        $this->_setName($schemaConfig->getName() ?: 'public');
72
73 1886
        foreach ($namespaces as $namespace) {
74 567
            $this->createNamespace($namespace);
75
        }
76
77 1886
        foreach ($tables as $table) {
78 1653
            $this->_addTable($table);
79
        }
80
81 1884
        foreach ($sequences as $sequence) {
82 1009
            $this->_addSequence($sequence);
83
        }
84 1882
    }
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 1856
    protected function _addTable(Table $table)
100
    {
101 1856
        $namespaceName = $table->getNamespaceName();
102 1856
        $tableName     = $table->getFullQualifiedName($this->getName());
103
104 1856
        if (isset($this->_tables[$tableName])) {
105 777
            throw SchemaException::tableAlreadyExists($tableName);
106
        }
107
108 1856
        if ($namespaceName !== null
109 1856
            && ! $table->isInDefaultNamespace($this->getName())
110 1856
            && ! $this->hasNamespace($namespaceName)) {
111 1116
            $this->createNamespace($namespaceName);
112
        }
113
114 1856
        $this->_tables[$tableName] = $table;
115 1856
        $table->setSchemaConfig($this->_schemaConfig);
116 1856
    }
117
118
    /**
119
     * @return void
120
     *
121
     * @throws SchemaException
122
     */
123 1403
    protected function _addSequence(Sequence $sequence)
124
    {
125 1403
        $namespaceName = $sequence->getNamespaceName();
126 1403
        $seqName       = $sequence->getFullQualifiedName($this->getName());
127
128 1403
        if (isset($this->_sequences[$seqName])) {
129 552
            throw SchemaException::sequenceAlreadyExists($seqName);
130
        }
131
132 1403
        if ($namespaceName !== null
133 1403
            && ! $sequence->isInDefaultNamespace($this->getName())
134 1403
            && ! $this->hasNamespace($namespaceName)) {
135 352
            $this->createNamespace($namespaceName);
136
        }
137
138 1403
        $this->_sequences[$seqName] = $sequence;
139 1403
    }
140
141
    /**
142
     * Returns the namespaces of this schema.
143
     *
144
     * @return string[] A list of namespace names.
145
     */
146 1638
    public function getNamespaces()
147
    {
148 1638
        return $this->namespaces;
149
    }
150
151
    /**
152
     * Gets all tables of this schema.
153
     *
154
     * @return Table[]
155
     */
156 1644
    public function getTables()
157
    {
158 1644
        return $this->_tables;
159
    }
160
161
    /**
162
     * @param string $tableName
163
     *
164
     * @return Table
165
     *
166
     * @throws SchemaException
167
     */
168 1650
    public function getTable($tableName)
169
    {
170 1650
        $tableName = $this->getFullQualifiedAssetName($tableName);
171 1650
        if (! isset($this->_tables[$tableName])) {
172 802
            throw SchemaException::tableDoesNotExist($tableName);
173
        }
174
175 1648
        return $this->_tables[$tableName];
176
    }
177
178
    /**
179
     * @param string $name
180
     *
181
     * @return string
182
     */
183 1707
    private function getFullQualifiedAssetName($name)
184
    {
185 1707
        $name = $this->getUnquotedAssetName($name);
186
187 1707
        if (strpos($name, '.') === false) {
188 1699
            $name = $this->getName() . '.' . $name;
189
        }
190
191 1707
        return strtolower($name);
192
    }
193
194
    /**
195
     * Returns the unquoted representation of a given asset name.
196
     *
197
     * @param string $assetName Quoted or unquoted representation of an asset name.
198
     *
199
     * @return string
200
     */
201 1717
    private function getUnquotedAssetName($assetName)
202
    {
203 1717
        if ($this->isIdentifierQuoted($assetName)) {
204 479
            return $this->trimQuotes($assetName);
205
        }
206
207 1715
        return $assetName;
208
    }
209
210
    /**
211
     * Does this schema have a namespace with the given name?
212
     *
213
     * @param string $namespaceName
214
     *
215
     * @return bool
216
     */
217 1253
    public function hasNamespace($namespaceName)
218
    {
219 1253
        $namespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
220
221 1253
        return isset($this->namespaces[$namespaceName]);
222
    }
223
224
    /**
225
     * Does this schema have a table with the given name?
226
     *
227
     * @param string $tableName
228
     *
229
     * @return bool
230
     */
231 1679
    public function hasTable($tableName)
232
    {
233 1679
        $tableName = $this->getFullQualifiedAssetName($tableName);
234
235 1679
        return isset($this->_tables[$tableName]);
236
    }
237
238
    /**
239
     * Gets all table names, prefixed with a schema name, even the default one if present.
240
     *
241
     * @return string[]
242
     */
243
    public function getTableNames()
244
    {
245
        return array_keys($this->_tables);
246
    }
247
248
    /**
249
     * @param string $sequenceName
250
     *
251
     * @return bool
252
     */
253 1276
    public function hasSequence($sequenceName)
254
    {
255 1276
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
256
257 1276
        return isset($this->_sequences[$sequenceName]);
258
    }
259
260
    /**
261
     * @param string $sequenceName
262
     *
263
     * @return Sequence
264
     *
265
     * @throws SchemaException
266
     */
267 1193
    public function getSequence($sequenceName)
268
    {
269 1193
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
270 1193
        if (! $this->hasSequence($sequenceName)) {
271 627
            throw SchemaException::sequenceDoesNotExist($sequenceName);
272
        }
273
274 1191
        return $this->_sequences[$sequenceName];
275
    }
276
277
    /**
278
     * @return Sequence[]
279
     */
280 1642
    public function getSequences()
281
    {
282 1642
        return $this->_sequences;
283
    }
284
285
    /**
286
     * Creates a new namespace.
287
     *
288
     * @param string $namespaceName The name of the namespace to create.
289
     *
290
     * @return \Doctrine\DBAL\Schema\Schema This schema instance.
291
     *
292
     * @throws SchemaException
293
     */
294 1293
    public function createNamespace($namespaceName)
295
    {
296 1293
        $unquotedNamespaceName = strtolower($this->getUnquotedAssetName($namespaceName));
297
298 1293
        if (isset($this->namespaces[$unquotedNamespaceName])) {
299 402
            throw SchemaException::namespaceAlreadyExists($unquotedNamespaceName);
300
        }
301
302 1293
        $this->namespaces[$unquotedNamespaceName] = $namespaceName;
303
304 1293
        return $this;
305
    }
306
307
    /**
308
     * Creates a new table.
309
     *
310
     * @param string $tableName
311
     *
312
     * @return Table
313
     */
314 1819
    public function createTable($tableName)
315
    {
316 1819
        $table = new Table($tableName);
317 1819
        $this->_addTable($table);
318
319 1819
        foreach ($this->_schemaConfig->getDefaultTableOptions() as $name => $value) {
320
            $table->addOption($name, $value);
321
        }
322
323 1819
        return $table;
324
    }
325
326
    /**
327
     * Renames a table.
328
     *
329
     * @param string $oldTableName
330
     * @param string $newTableName
331
     *
332
     * @return \Doctrine\DBAL\Schema\Schema
333
     */
334 752
    public function renameTable($oldTableName, $newTableName)
335
    {
336 752
        $table = $this->getTable($oldTableName);
337 752
        $table->_setName($newTableName);
0 ignored issues
show
Bug introduced by
The method _setName() cannot be called from this context as it is declared protected in class Doctrine\DBAL\Schema\AbstractAsset.

This check looks for access to methods that are not accessible from the current context.

If you need to make a method accessible to another context you can raise its visibility level in the defining class.

Loading history...
338
339 752
        $this->dropTable($oldTableName);
340 752
        $this->_addTable($table);
341
342 752
        return $this;
343
    }
344
345
    /**
346
     * Drops a table from the schema.
347
     *
348
     * @param string $tableName
349
     *
350
     * @return \Doctrine\DBAL\Schema\Schema
351
     */
352 760
    public function dropTable($tableName)
353
    {
354 760
        $tableName = $this->getFullQualifiedAssetName($tableName);
355 760
        $this->getTable($tableName);
356 760
        unset($this->_tables[$tableName]);
357
358 760
        return $this;
359
    }
360
361
    /**
362
     * Creates a new sequence.
363
     *
364
     * @param string $sequenceName
365
     * @param int    $allocationSize
366
     * @param int    $initialValue
367
     *
368
     * @return Sequence
369
     */
370 1276
    public function createSequence($sequenceName, $allocationSize = 1, $initialValue = 1)
371
    {
372 1276
        $seq = new Sequence($sequenceName, $allocationSize, $initialValue);
373 1276
        $this->_addSequence($seq);
374
375 1276
        return $seq;
376
    }
377
378
    /**
379
     * @param string $sequenceName
380
     *
381
     * @return \Doctrine\DBAL\Schema\Schema
382
     */
383 577
    public function dropSequence($sequenceName)
384
    {
385 577
        $sequenceName = $this->getFullQualifiedAssetName($sequenceName);
386 577
        unset($this->_sequences[$sequenceName]);
387
388 577
        return $this;
389
    }
390
391
    /**
392
     * Returns an array of necessary SQL queries to create the schema on the given platform.
393
     *
394
     * @return string[]
395
     */
396 1773
    public function toSql(AbstractPlatform $platform)
397
    {
398 1773
        $sqlCollector = new CreateSchemaSqlCollector($platform);
399 1773
        $this->visit($sqlCollector);
400
401 1773
        return $sqlCollector->getQueries();
402
    }
403
404
    /**
405
     * Return an array of necessary SQL queries to drop the schema on the given platform.
406
     *
407
     * @return string[]
408
     */
409 137
    public function toDropSql(AbstractPlatform $platform)
410
    {
411 137
        $dropSqlCollector = new DropSchemaSqlCollector($platform);
412 137
        $this->visit($dropSqlCollector);
413
414 137
        return $dropSqlCollector->getQueries();
415
    }
416
417
    /**
418
     * @return string[]
419
     */
420 896
    public function getMigrateToSql(Schema $toSchema, AbstractPlatform $platform)
421
    {
422 896
        $comparator = new Comparator();
423 896
        $schemaDiff = $comparator->compare($this, $toSchema);
424
425 896
        return $schemaDiff->toSql($platform);
426
    }
427
428
    /**
429
     * @return string[]
430
     */
431
    public function getMigrateFromSql(Schema $fromSchema, AbstractPlatform $platform)
432
    {
433
        $comparator = new Comparator();
434
        $schemaDiff = $comparator->compare($fromSchema, $this);
435
436
        return $schemaDiff->toSql($platform);
437
    }
438
439
    /**
440
     * @return void
441
     */
442 1786
    public function visit(Visitor $visitor)
443
    {
444 1786
        $visitor->acceptSchema($this);
445
446 1786
        if ($visitor instanceof NamespaceVisitor) {
447 1780
            foreach ($this->namespaces as $namespace) {
448 308
                $visitor->acceptNamespace($namespace);
449
            }
450
        }
451
452 1786
        foreach ($this->_tables as $table) {
453 1785
            $table->visit($visitor);
454
        }
455
456 1786
        foreach ($this->_sequences as $sequence) {
457 333
            $sequence->visit($visitor);
458
        }
459 1786
    }
460
461
    /**
462
     * Cloning a Schema triggers a deep clone of all related assets.
463
     *
464
     * @return void
465
     */
466 1434
    public function __clone()
467
    {
468 1434
        foreach ($this->_tables as $k => $table) {
469 1146
            $this->_tables[$k] = clone $table;
470
        }
471 1434
        foreach ($this->_sequences as $k => $sequence) {
472 1154
            $this->_sequences[$k] = clone $sequence;
473
        }
474 1434
    }
475
}
476