Failed Conditions
Pull Request — develop (#3348)
by Sergei
125:02 queued 59:58
created

Schema::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

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