Failed Conditions
Pull Request — develop (#3348)
by Sergei
62:00
created

Table::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Doctrine\DBAL\Schema;
6
7
use Doctrine\DBAL\DBALException;
8
use Doctrine\DBAL\Schema\Visitor\Visitor;
9
use Doctrine\DBAL\Types\Type;
10
use function array_keys;
11
use function array_merge;
12
use function array_search;
13
use function array_unique;
14
use function assert;
15
use function in_array;
16
use function is_string;
17
use function preg_match;
18
use function strlen;
19
use function strtolower;
20
use function uksort;
21
22
/**
23
 * Object Representation of a table.
24
 */
25
class Table extends AbstractAsset
26
{
27
    /** @var Column[] */
28
    protected $_columns = [];
29
30
    /** @var Index[] */
31
    private $implicitIndexes = [];
32
33
    /** @var Index[] */
34
    protected $_indexes = [];
35
36
    /** @var string */
37
    protected $_primaryKeyName = false;
38
39
    /** @var UniqueConstraint[] */
40
    protected $_uniqueConstraints = [];
41
42
    /** @var ForeignKeyConstraint[] */
43
    protected $_fkConstraints = [];
44
45
    /** @var mixed[] */
46
    protected $_options = [];
47
48
    /** @var SchemaConfig|null */
49
    protected $_schemaConfig = null;
50
51
    /**
52
     * @param Column[]               $columns
53
     * @param Index[]                $indexes
54
     * @param UniqueConstraint[]     $uniqueConstraints
55
     * @param ForeignKeyConstraint[] $fkConstraints
56
     * @param mixed[]                $options
57
     *
58
     * @throws DBALException
59
     */
60 18969
    public function __construct(
61
        string $tableName,
62
        array $columns = [],
63
        array $indexes = [],
64
        array $uniqueConstraints = [],
65
        array $fkConstraints = [],
66
        array $options = []
67
    ) {
68 18969
        if (strlen($tableName) === 0) {
69 1603
            throw DBALException::invalidTableName($tableName);
70
        }
71
72 18967
        $this->_setName($tableName);
73
74 18967
        foreach ($columns as $column) {
75 16855
            $this->_addColumn($column);
76
        }
77
78 18965
        foreach ($indexes as $idx) {
79 16738
            $this->_addIndex($idx);
80
        }
81
82 18961
        foreach ($uniqueConstraints as $uniqueConstraint) {
83
            $this->_addUniqueConstraint($uniqueConstraint);
84
        }
85
86 18961
        foreach ($fkConstraints as $fkConstraint) {
87 15943
            $this->_addForeignKeyConstraint($fkConstraint);
88
        }
89
90 18961
        $this->_options = $options;
91 18961
    }
92
93
    public function getName() : string
94
    {
95
        $name = parent::getName();
96 17365
        assert(is_string($name));
97
98 17365
        return $name;
99 17365
    }
100
101
    public function setSchemaConfig(SchemaConfig $schemaConfig) : void
102
    {
103
        $this->_schemaConfig = $schemaConfig;
104
    }
105
106
    /**
107
     * Sets the Primary Key.
108
     *
109 18169
     * @param string[]     $columnNames
110
     * @param string|false $indexName
111 18169
     */
112
    public function setPrimaryKey(array $columnNames, $indexName = false) : self
113 18169
    {
114 18169
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
115 18169
116
        foreach ($columnNames as $columnName) {
117
            $column = $this->getColumn($columnName);
118 18169
            $column->setNotnull(true);
119
        }
120
121
        return $this;
122
    }
123
124
    /**
125
     * @param mixed[]  $columnNames
126
     * @param string[] $flags
127
     * @param mixed[]  $options
128
     */
129
    public function addUniqueConstraint(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
130
    {
131
        if ($indexName === null) {
132
            $indexName = $this->_generateIdentifierName(
133
                array_merge([$this->getName()], $columnNames),
134
                'uniq',
135
                $this->_getMaxIdentifierLength()
136
            );
137
        }
138
139
        return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options));
140
    }
141
142
    /**
143
     * @param string[] $columnNames
144
     * @param string[] $flags
145
     * @param mixed[]  $options
146
     */
147
    public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
148
    {
149
        if ($indexName === null) {
150 16399
            $indexName = $this->_generateIdentifierName(
151
                array_merge([$this->getName()], $columnNames),
152 16399
                'idx',
153 15841
                $this->_getMaxIdentifierLength()
154 15841
            );
155 15841
        }
156 15841
157
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
158
    }
159
160 16399
    /**
161
     * Drops the primary key from this table.
162
     */
163
    public function dropPrimaryKey() : void
164
    {
165
        $this->dropIndex($this->_primaryKeyName);
166
        $this->_primaryKeyName = false;
0 ignored issues
show
Documentation Bug introduced by
The property $_primaryKeyName was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
167
    }
168 15053
169
    /**
170 15053
     * Drops an index from this table.
171 15053
     *
172 15053
     * @param string $indexName The index name.
173
     *
174
     * @throws SchemaException If the index does not exist.
175
     */
176
    public function dropIndex(string $indexName) : void
177
    {
178
        $indexName = $this->normalizeIdentifier($indexName);
179
180
        if (! $this->hasIndex($indexName)) {
181
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
182
        }
183 15090
184
        unset($this->_indexes[$indexName]);
185 15090
    }
186
187 15090
    /**
188
     * @param string[] $columnNames
189
     * @param mixed[]  $options
190
     */
191 15090
    public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []) : self
192 15090
    {
193
        if ($indexName === null) {
194
            $indexName = $this->_generateIdentifierName(
195
                array_merge([$this->getName()], $columnNames),
196
                'uniq',
197
                $this->_getMaxIdentifierLength()
198
            );
199
        }
200
201 17207
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
202
    }
203 17207
204 16864
    /**
205 16864
     * Renames an index.
206 16864
     *
207 16864
     * @param string      $oldIndexName The name of the index to rename from.
208
     * @param string|null $newIndexName The name of the index to rename to.
209
     *                                  If null is given, the index name will be auto-generated.
210
     *
211 17207
     * @return self This table instance.
212
     *
213
     * @throws SchemaException If no index exists for the given current name
214
     *                         or if an index with the given new name already exists on this table.
215
     */
216
    public function renameIndex(string $oldIndexName, ?string $newIndexName = null) : self
217
    {
218
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
219
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
220
221
        if ($oldIndexName === $normalizedNewIndexName) {
222
            return $this;
223
        }
224
225
        if (! $this->hasIndex($oldIndexName)) {
226 14393
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
227
        }
228 14393
229 14393
        if ($this->hasIndex($normalizedNewIndexName)) {
230
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
231 14393
        }
232 469
233
        $oldIndex = $this->_indexes[$oldIndexName];
234
235 14393
        if ($oldIndex->isPrimary()) {
236 378
            $this->dropPrimaryKey();
237
238
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
239 14391
        }
240 353
241
        unset($this->_indexes[$oldIndexName]);
242
243 14389
        if ($oldIndex->isUnique()) {
244
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
245 14389
        }
246 453
247
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
248 453
    }
249
250
    /**
251 14389
     * Checks if an index begins in the order of the given columns.
252
     *
253 14389
     * @param string[] $columnNames
254 455
     */
255
    public function columnsAreIndexed(array $columnNames) : bool
256
    {
257 14387
        foreach ($this->getIndexes() as $index) {
258
            /** @var $index Index */
259
            if ($index->spansColumns($columnNames)) {
260
                return true;
261
            }
262
        }
263
264
        return false;
265
    }
266
267 14565
    /**
268
     * @param mixed[] $options
269 14565
     */
270
    public function addColumn(string $columnName, string $typeName, array $options = []) : Column
271 14565
    {
272 14565
        $column = new Column($columnName, Type::getType($typeName), $options);
273
274
        $this->_addColumn($column);
275
276
        return $column;
277
    }
278
279
    /**
280
     * Renames a Column.
281
     *
282
     * @deprecated
283
     *
284
     * @throws DBALException
285
     */
286 18729
    public function renameColumn(string $oldColumnName, string $newColumnName) : void
0 ignored issues
show
Unused Code introduced by
The parameter $oldColumnName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

286
    public function renameColumn(/** @scrutinizer ignore-unused */ string $oldColumnName, string $newColumnName) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $newColumnName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

286
    public function renameColumn(string $oldColumnName, /** @scrutinizer ignore-unused */ string $newColumnName) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
287
    {
288 18729
        throw new DBALException(
289
            'Table#renameColumn() was removed, because it drops and recreates the column instead. ' .
290 18729
            'There is no fix available, because a schema diff cannot reliably detect if a column ' .
291
            'was renamed or one column was created and another one dropped.'
292 18729
        );
293
    }
294
295
    /**
296
     * Change Column Details.
297
     *
298
     * @param mixed[] $options
299
     */
300
    public function changeColumn(string $columnName, array $options) : self
301
    {
302
        $column = $this->getColumn($columnName);
303
304
        $column->setOptions($options);
305
306
        return $this;
307
    }
308
309
    /**
310
     * Drops a Column from the Table.
311
     */
312
    public function dropColumn(string $columnName) : self
313
    {
314
        $columnName = $this->normalizeIdentifier($columnName);
315
316
        unset($this->_columns[$columnName]);
317
318
        return $this;
319
    }
320
321
    /**
322 15420
     * Adds a foreign key constraint.
323
     *
324 15420
     * Name is inferred from the local columns.
325
     *
326 15420
     * @param Table|string $foreignTable       Table schema instance or table name
327
     * @param string[]     $localColumnNames
328 15420
     * @param string[]     $foreignColumnNames
329
     * @param mixed[]      $options
330
     */
331
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], ?string $constraintName = null) : self
332
    {
333
        if (! $constraintName) {
334
            $constraintName = $this->_generateIdentifierName(
335
                array_merge((array) $this->getName(), $localColumnNames),
336
                'fk',
337
                $this->_getMaxIdentifierLength()
338 1494
            );
339
        }
340 1494
341
        return $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);
1 ignored issue
show
Deprecated Code introduced by
The function Doctrine\DBAL\Schema\Tab...dForeignKeyConstraint() has been deprecated: Use {@link addForeignKeyConstraint} ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

341
        return /** @scrutinizer ignore-deprecated */ $this->addNamedForeignKeyConstraint($constraintName, $foreignTable, $localColumnNames, $foreignColumnNames, $options);

This function has been deprecated. The supplier of the function has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed and what other function to use instead.

Loading history...
342 1494
    }
343
344 1494
    /**
345
     * Adds a foreign key constraint.
346
     *
347
     * Name is to be generated by the database itself.
348
     *
349
     * @deprecated Use {@link addForeignKeyConstraint}
350
     *
351
     * @param Table|string $foreignTable       Table schema instance or table name
352
     * @param string[]     $localColumnNames
353
     * @param string[]     $foreignColumnNames
354
     * @param mixed[]      $options
355
     */
356
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = []) : self
357
    {
358
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
359
    }
360 17211
361
    /**
362 17211
     * Adds a foreign key constraint with a given name.
363 17119
     *
364 17119
     * @deprecated Use {@link addForeignKeyConstraint}
365 17119
     *
366 17119
     * @param Table|string $foreignTable       Table schema instance or table name
367
     * @param string[]     $localColumnNames
368
     * @param string[]     $foreignColumnNames
369
     * @param mixed[]      $options
370 17211
     *
371
     * @throws SchemaException
372
     */
373
    public function addNamedForeignKeyConstraint(string $name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = []) : self
374
    {
375
        if ($foreignTable instanceof Table) {
376
            foreach ($foreignColumnNames as $columnName) {
377
                if (! $foreignTable->hasColumn($columnName)) {
378
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
379
                }
380
            }
381
        }
382
383
        foreach ($localColumnNames as $columnName) {
384
            if (! $this->hasColumn($columnName)) {
385
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
386
            }
387 10648
        }
388
389 10648
        $constraint = new ForeignKeyConstraint(
390
            $localColumnNames,
391
            $foreignTable,
392
            $foreignColumnNames,
393
            $name,
394
            $options
395
        );
396
397
        return $this->_addForeignKeyConstraint($constraint);
398
    }
399
400
    /**
401
     * @param mixed $value
402
     */
403
    public function addOption(string $name, $value) : self
404
    {
405
        $this->_options[$name] = $value;
406
407 17213
        return $this;
408
    }
409 17213
410 16735
    /**
411 16735
     * Returns whether this table has a foreign key constraint with the given name.
412 1118
     */
413
    public function hasForeignKey(string $constraintName) : bool
414
    {
415
        $constraintName = $this->normalizeIdentifier($constraintName);
416
417 17211
        return isset($this->_fkConstraints[$constraintName]);
418 17211
    }
419 1231
420
    /**
421
     * Returns the foreign key constraint with the given name.
422
     *
423 17209
     * @param string $constraintName The constraint name.
424 17209
     *
425 178
     * @throws SchemaException If the foreign key does not exist.
426 178
     */
427 178
    public function getForeignKey(string $constraintName) : ForeignKeyConstraint
428 178
    {
429
        $constraintName = $this->normalizeIdentifier($constraintName);
430
431 17209
        if (! $this->hasForeignKey($constraintName)) {
432
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
433
        }
434
435
        return $this->_fkConstraints[$constraintName];
436
    }
437
438
    /**
439
     * Removes the foreign key constraint with the given name.
440 15342
     *
441
     * @param string $constraintName The constraint name.
442 15342
     *
443
     * @throws SchemaException
444 15342
     */
445
    public function removeForeignKey(string $constraintName) : void
446
    {
447
        $constraintName = $this->normalizeIdentifier($constraintName);
448
449
        if (! $this->hasForeignKey($constraintName)) {
450
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
451
        }
452
453
        unset($this->_fkConstraints[$constraintName]);
454 14379
    }
455
456 14379
    /**
457
     * Returns whether this table has a unique constraint with the given name.
458 14379
     */
459
    public function hasUniqueConstraint(string $constraintName) : bool
460
    {
461
        $constraintName = $this->normalizeIdentifier($constraintName);
462
463
        return isset($this->_uniqueConstraints[$constraintName]);
464
    }
465
466
    /**
467
     * Returns the unique constraint with the given name.
468
     *
469
     * @param string $constraintName The constraint name.
470 344
     *
471
     * @throws SchemaException If the foreign key does not exist.
472 344
     */
473
    public function getUniqueConstraint(string $constraintName) : UniqueConstraint
474 344
    {
475
        $constraintName = $this->normalizeIdentifier($constraintName);
476
477
        if (! $this->hasUniqueConstraint($constraintName)) {
478 344
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
479
        }
480
481
        return $this->_uniqueConstraints[$constraintName];
482
    }
483
484
    /**
485
     * Removes the unique constraint with the given name.
486
     *
487
     * @param string $constraintName The constraint name.
488
     *
489
     * @throws SchemaException
490 346
     */
491
    public function removeUniqueConstraint(string $constraintName) : void
492 346
    {
493
        $constraintName = $this->normalizeIdentifier($constraintName);
494 346
495
        if (! $this->hasUniqueConstraint($constraintName)) {
496
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
497
        }
498 346
499 346
        unset($this->_uniqueConstraints[$constraintName]);
500
    }
501
502
    /**
503
     * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
504
     *
505
     * @return Column[]
506
     */
507
    public function getColumns() : array
508
    {
509
        $columns = $this->_columns;
510
        $pkCols  = [];
511
        $fkCols  = [];
512
513
        $primaryKey = $this->getPrimaryKey();
514
515
        if ($primaryKey !== null) {
516
            $pkCols = $primaryKey->getColumns();
517
        }
518
519
        foreach ($this->getForeignKeys() as $fk) {
520
            /** @var ForeignKeyConstraint $fk */
521
            $fkCols = array_merge($fkCols, $fk->getColumns());
522
        }
523
524
        $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
525
526
        uksort($columns, static function ($a, $b) use ($colNames) : bool {
527
            return array_search($a, $colNames) >= array_search($b, $colNames);
528
        });
529
530
        return $columns;
531
    }
532
533
    /**
534
     * Returns whether this table has a Column with the given name.
535
     *
536
     * @param string $columnName The column name.
537
     */
538
    public function hasColumn(string $columnName) : bool
539
    {
540
        $columnName = $this->normalizeIdentifier($columnName);
541
542
        return isset($this->_columns[$columnName]);
543
    }
544
545
    /**
546
     * Returns the Column with the given name.
547
     *
548
     * @param string $columnName The column name.
549
     *
550
     * @throws SchemaException If the column does not exist.
551
     */
552
    public function getColumn(string $columnName) : Column
553
    {
554
        $columnName = $this->normalizeIdentifier($columnName);
555
556
        if (! $this->hasColumn($columnName)) {
557
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
558
        }
559
560 18483
        return $this->_columns[$columnName];
561
    }
562 18483
563 18483
    /**
564 18483
     * Returns the primary key.
565
     *
566 18483
     * @return Index|null The primary key, or null if this Table has no primary key.
567
     */
568 18483
    public function getPrimaryKey() : ?Index
569 18007
    {
570
        return $this->hasPrimaryKey()
571
            ? $this->getIndex($this->_primaryKeyName)
572 18483
            : null;
573
    }
574 17279
575
    /**
576
     * Returns the primary key columns.
577 18483
     *
578
     * @return string[]
579
     *
580 18161
     * @throws DBALException
581 18483
     */
582
    public function getPrimaryKeyColumns() : array
583 18483
    {
584
        $primaryKey = $this->getPrimaryKey();
585
586
        if ($primaryKey === null) {
587
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
588
        }
589
590
        return $primaryKey->getColumns();
591
    }
592
593 18537
    /**
594
     * Returns whether this table has a primary key.
595 18537
     */
596
    public function hasPrimaryKey() : bool
597 18537
    {
598
        return $this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName);
599
    }
600
601
    /**
602
     * Returns whether this table has an Index with the given name.
603
     *
604
     * @param string $indexName The index name.
605
     */
606
    public function hasIndex(string $indexName) : bool
607
    {
608
        $indexName = $this->normalizeIdentifier($indexName);
609 18329
610
        return isset($this->_indexes[$indexName]);
611 18329
    }
612
613 18329
    /**
614 1453
     * Returns the Index with the given name.
615
     *
616
     * @param string $indexName The index name.
617 18327
     *
618
     * @throws SchemaException If the index does not exist.
619
     */
620
    public function getIndex(string $indexName) : Index
621
    {
622
        $indexName = $this->normalizeIdentifier($indexName);
623
624
        if (! $this->hasIndex($indexName)) {
625 18493
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
626
        }
627 18493
628 18017
        return $this->_indexes[$indexName];
629 18493
    }
630
631
    /**
632
     * @return Index[]
633
     */
634
    public function getIndexes() : array
635
    {
636
        return $this->_indexes;
637
    }
638
639 15043
    /**
640
     * Returns the unique constraints.
641 15043
     *
642
     * @return UniqueConstraint[]
643 15043
     */
644
    public function getUniqueConstraints() : array
645
    {
646
        return $this->_uniqueConstraints;
647 15043
    }
648
649
    /**
650
     * Returns the foreign key constraints.
651
     *
652
     * @return ForeignKeyConstraint[]
653
     */
654
    public function getForeignKeys() : array
655 18499
    {
656
        return $this->_fkConstraints;
657 18499
    }
658
659
    public function hasOption(string $name) : bool
660
    {
661
        return isset($this->_options[$name]);
662
    }
663
664
    /**
665
     * @return mixed
666
     */
667 18121
    public function getOption(string $name)
668
    {
669 18121
        return $this->_options[$name];
670
    }
671 18121
672
    /**
673
     * @return mixed[]
674
     */
675
    public function getOptions() : array
676
    {
677
        return $this->_options;
678
    }
679
680
    public function visit(Visitor $visitor) : void
681
    {
682
        $visitor->acceptTable($this);
683 18077
684
        foreach ($this->getColumns() as $column) {
685 18077
            $visitor->acceptColumn($this, $column);
686
        }
687 18077
688 1328
        foreach ($this->getIndexes() as $index) {
689
            $visitor->acceptIndex($this, $index);
690
        }
691 18075
692
        foreach ($this->getForeignKeys() as $constraint) {
693
            $visitor->acceptForeignKey($this, $constraint);
694
        }
695
    }
696
697 18457
    /**
698
     * Clone of a Table triggers a deep clone of all affected assets.
699 18457
     */
700
    public function __clone()
701
    {
702
        foreach ($this->_columns as $k => $column) {
703
            $this->_columns[$k] = clone $column;
704
        }
705
706
        foreach ($this->_indexes as $k => $index) {
707 18235
            $this->_indexes[$k] = clone $index;
708
        }
709 18235
710
        foreach ($this->_fkConstraints as $k => $fk) {
711
            $this->_fkConstraints[$k] = clone $fk;
712
            $this->_fkConstraints[$k]->setLocalTable($this);
713
        }
714
    }
715
716
    protected function _getMaxIdentifierLength() : int
717 18519
    {
718
        return $this->_schemaConfig instanceof SchemaConfig
719 18519
            ? $this->_schemaConfig->getMaxIdentifierLength()
720
            : 63;
721
    }
722
723
    /**
724
     * @throws SchemaException
725
     */
726
    protected function _addColumn(Column $column) : void
727 15207
    {
728
        $columnName = $column->getName();
729 15207
        $columnName = $this->normalizeIdentifier($columnName);
730
731
        if (isset($this->_columns[$columnName])) {
732
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
733
        }
734
735
        $this->_columns[$columnName] = $column;
736
    }
737 14699
738
    /**
739 14699
     * Adds an index to the table.
740
     *
741
     * @throws SchemaException
742
     */
743
    protected function _addIndex(Index $indexCandidate) : self
744
    {
745 18263
        $indexName               = $indexCandidate->getName();
746
        $indexName               = $this->normalizeIdentifier($indexName);
747 18263
        $replacedImplicitIndexes = [];
748
749
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
750
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
751
                continue;
752
            }
753 17097
754
            $replacedImplicitIndexes[] = $name;
755 17097
        }
756
757 17097
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
758 17091
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
759
        ) {
760
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
761 17097
        }
762 15539
763
        foreach ($replacedImplicitIndexes as $name) {
764
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
765 17097
        }
766 158
767
        if ($indexCandidate->isPrimary()) {
768 17097
            $this->_primaryKeyName = $indexName;
769
        }
770
771
        $this->_indexes[$indexName] = $indexCandidate;
772
773
        return $this;
774
    }
775 16464
776
    protected function _addUniqueConstraint(UniqueConstraint $constraint) : self
777 16464
    {
778 16462
        $name = $constraint->getName() ?? $this->_generateIdentifierName(
779
            array_merge([$this->getName()], $constraint->getLocalColumns()),
0 ignored issues
show
Bug introduced by
The method getLocalColumns() does not exist on Doctrine\DBAL\Schema\UniqueConstraint. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

779
            array_merge([$this->getName()], $constraint->/** @scrutinizer ignore-call */ getLocalColumns()),

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
780
            'fk',
781 16464
            $this->_getMaxIdentifierLength()
782 16440
        );
783
784
        $name = $this->normalizeIdentifier($name);
785 16464
786 15333
        $this->_uniqueConstraints[$name] = $constraint;
787 15333
788
        // If there is already an index that fulfills this requirements drop the request. In the case of __construct
789 16464
        // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates.
790
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
791
        $indexName = $this->_generateIdentifierName(
792
            array_merge([$this->getName()], $constraint->getColumns()),
793
            'idx',
794 17543
            $this->_getMaxIdentifierLength()
795
        );
796 17543
797 17161
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false);
798 17543
799
        foreach ($this->_indexes as $existingIndex) {
800
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
801
                return $this;
802
            }
803
        }
804
805
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
806 18787
807
        return $this;
808 18787
    }
809 18787
810
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) : self
811 18787
    {
812 1428
        $constraint->setLocalTable($this);
813
814
        $name = $constraint->getName() ?? $this->_generateIdentifierName(
815 18787
            array_merge([$this->getName()], $constraint->getLocalColumns()),
816 18787
            'fk',
817
            $this->_getMaxIdentifierLength()
818
        );
819
820
        $name = $this->normalizeIdentifier($name);
821
822
        $this->_fkConstraints[$name] = $constraint;
823
824
        // add an explicit index on the foreign key columns.
825 18417
        // If there is already an index that fulfills this requirements drop the request. In the case of __construct
826
        // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates.
827 18417
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
828 18417
        $indexName = $this->_generateIdentifierName(
829 18417
            array_merge([$this->getName()], $constraint->getColumns()),
830
            'idx',
831 18417
            $this->_getMaxIdentifierLength()
832 13821
        );
833 13813
834
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
835
836 734
        foreach ($this->_indexes as $existingIndex) {
837
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
838
                return $this;
839 18417
            }
840 18417
        }
841
842 1305
        $this->_addIndex($indexCandidate);
843
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
844
845 18417
        return $this;
846 734
    }
847
848
    /**
849 18417
     * Normalizes a given identifier.
850 18173
     *
851
     * Trims quotes and lowercases the given identifier.
852
     *
853 18417
     * @param string|null $identifier The identifier to normalize.
854
     *
855 18417
     * @return string The normalized identifier.
856
     */
857
    private function normalizeIdentifier(?string $identifier) : string
858
    {
859
        if ($identifier === null) {
860
            return '';
861
        }
862
863
        return $this->trimQuotes(strtolower($identifier));
864
    }
865
866
    /**
867
     * @param mixed[] $columns
868
     * @param mixed[] $flags
869
     * @param mixed[] $options
870
     *
871
     * @throws SchemaException
872
     */
873
    private function _createUniqueConstraint(array $columns, string $indexName, array $flags = [], array $options = []) : UniqueConstraint
874
    {
875
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
876
            throw SchemaException::indexNameInvalid($indexName);
877
        }
878
879
        foreach ($columns as $index => $value) {
880
            if (is_string($index)) {
881
                $columnName = $index;
882
            } else {
883
                $columnName = $value;
884
            }
885
886
            if (! $this->hasColumn($columnName)) {
887
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
888
            }
889
        }
890
891
        return new UniqueConstraint($indexName, $columns, $flags, $options);
892
    }
893
894
    /**
895
     * @param mixed[]  $columns
896
     * @param string[] $flags
897
     * @param mixed[]  $options
898
     *
899
     * @throws SchemaException
900 17379
     */
901
    private function _createIndex(array $columns, string $indexName, bool $isUnique, bool $isPrimary, array $flags = [], array $options = []) : Index
902 17379
    {
903
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
904 17379
            throw SchemaException::indexNameInvalid($indexName);
905 17217
        }
906 2397
907 2397
        foreach ($columns as $index => $value) {
908 2397
            if (is_string($index)) {
909 17379
                $columnName = $index;
910
            } else {
911
                $columnName = $value;
912 17379
            }
913
914 17379
            if (! $this->hasColumn($columnName)) {
915
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
916
            }
917
        }
918
919
        return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
920 17379
    }
921
}
922