Failed Conditions
Pull Request — develop (#3348)
by Sergei
65:23
created

Table::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
ccs 3
cts 3
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 17654
     * @throws DBALException
59
     */
60
    public function __construct(
61
        string $tableName,
62
        array $columns = [],
63
        array $indexes = [],
64
        array $uniqueConstraints = [],
65
        array $fkConstraints = [],
66 17654
        array $options = []
67 1561
    ) {
68
        if (strlen($tableName) === 0) {
69
            throw DBALException::invalidTableName($tableName);
70 17653
        }
71
72 17653
        $this->_setName($tableName);
73 16192
74
        foreach ($columns as $column) {
75
            $this->_addColumn($column);
76 17652
        }
77 16119
78
        foreach ($indexes as $idx) {
79
            $this->_addIndex($idx);
80 17650
        }
81
82
        foreach ($uniqueConstraints as $uniqueConstraint) {
83
            $this->_addUniqueConstraint($uniqueConstraint);
84 17650
        }
85 15348
86
        foreach ($fkConstraints as $fkConstraint) {
87
            $this->_addForeignKeyConstraint($fkConstraint);
88 17650
        }
89 17650
90
        $this->_options = $options;
91
    }
92
93
    public function getName() : string
94 16674
    {
95
        $name = parent::getName();
96 16674
        assert(is_string($name));
97 16674
98
        return $name;
99
    }
100
101
    public function setSchemaConfig(SchemaConfig $schemaConfig) : void
102
    {
103
        $this->_schemaConfig = $schemaConfig;
104
    }
105
106
    /**
107 17254
     * Sets the Primary Key.
108
     *
109 17254
     * @param string[]     $columnNames
110
     * @param string|false $indexName
111 17254
     */
112 17254
    public function setPrimaryKey(array $columnNames, $indexName = false) : self
113 17254
    {
114
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
115
116 17254
        foreach ($columnNames as $columnName) {
117
            $column = $this->getColumn($columnName);
118
            $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 15724
    {
149
        if ($indexName === null) {
150 15724
            $indexName = $this->_generateIdentifierName(
151 15322
                array_merge([$this->getName()], $columnNames),
152 15322
                'idx',
153 15322
                $this->_getMaxIdentifierLength()
154 15322
            );
155
        }
156
157
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
158 15724
    }
159
160
    /**
161
     * Drops the primary key from this table.
162
     */
163
    public function dropPrimaryKey() : void
164
    {
165
        $this->dropIndex($this->_primaryKeyName);
166 14581
        $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 14581
169 14581
    /**
170 14581
     * Drops an index from this table.
171
     *
172
     * @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 14604
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
182
        }
183 14604
184
        unset($this->_indexes[$indexName]);
185 14604
    }
186
187
    /**
188
     * @param string[] $columnNames
189 14604
     * @param mixed[]  $options
190 14604
     */
191
    public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []) : self
192
    {
193
        if ($indexName === null) {
194
            $indexName = $this->_generateIdentifierName(
195
                array_merge([$this->getName()], $columnNames),
196
                'uniq',
197
                $this->_getMaxIdentifierLength()
198
            );
199 16559
        }
200
201 16559
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
202 16227
    }
203 16227
204 16227
    /**
205 16227
     * Renames an index.
206
     *
207
     * @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 16559
     *                                  If null is given, the index name will be auto-generated.
210
     *
211
     * @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 14401
225
        if (! $this->hasIndex($oldIndexName)) {
226 14401
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
227 14401
        }
228
229 14401
        if ($this->hasIndex($normalizedNewIndexName)) {
230 441
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
231
        }
232
233 14401
        $oldIndex = $this->_indexes[$oldIndexName];
234 361
235
        if ($oldIndex->isPrimary()) {
236
            $this->dropPrimaryKey();
237 14400
238 337
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
239
        }
240
241 14399
        unset($this->_indexes[$oldIndexName]);
242
243 14399
        if ($oldIndex->isUnique()) {
244 433
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
245
        }
246 433
247
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
248
    }
249 14399
250
    /**
251 14399
     * Checks if an index begins in the order of the given columns.
252 434
     *
253
     * @param string[] $columnNames
254
     */
255 14398
    public function columnsAreIndexed(array $columnNames) : bool
256
    {
257
        foreach ($this->getIndexes() as $index) {
258
            /** @var $index Index */
259
            if ($index->spansColumns($columnNames)) {
260
                return true;
261
            }
262
        }
263
264
        return false;
265 14581
    }
266
267 14581
    /**
268
     * @param mixed[] $options
269 14581
     */
270 14581
    public function addColumn(string $columnName, string $typeName, array $options = []) : Column
271
    {
272
        $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 17534
     * @throws DBALException
285
     */
286 17534
    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 17534
        throw new DBALException(
289
            'Table#renameColumn() was removed, because it drops and recreates the column instead. ' .
290 17534
            '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
        );
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 14833
321
    /**
322 14833
     * Adds a foreign key constraint.
323
     *
324 14833
     * Name is inferred from the local columns.
325
     *
326 14833
     * @param Table|string $foreignTable       Table schema instance or table name
327
     * @param string[]     $localColumnNames
328
     * @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 1449
                'fk',
337
                $this->_getMaxIdentifierLength()
338 1449
            );
339
        }
340 1449
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 1449
    }
343
344
    /**
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 16626
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
359
    }
360 16626
361 16580
    /**
362 16580
     * Adds a foreign key constraint with a given name.
363 16580
     *
364 16580
     * @deprecated Use {@link addForeignKeyConstraint}
365
     *
366
     * @param Table|string $foreignTable       Table schema instance or table name
367
     * @param string[]     $localColumnNames
368 16626
     * @param string[]     $foreignColumnNames
369
     * @param mixed[]      $options
370
     *
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 10316
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
386
            }
387 10316
        }
388
389
        $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 16627
        $this->_options[$name] = $value;
406
407 16627
        return $this;
408 16220
    }
409 16220
410 1054
    /**
411
     * Returns whether this table has a foreign key constraint with the given name.
412
     */
413
    public function hasForeignKey(string $constraintName) : bool
414
    {
415 16626
        $constraintName = $this->normalizeIdentifier($constraintName);
416 16626
417 1122
        return isset($this->_fkConstraints[$constraintName]);
418
    }
419
420
    /**
421 16625
     * Returns the foreign key constraint with the given name.
422 15999
     *
423 89
     * @param string $constraintName The constraint name.
424 89
     *
425 89
     * @throws SchemaException If the foreign key does not exist.
426 715
     */
427
    public function getForeignKey(string $constraintName) : ForeignKeyConstraint
428
    {
429 16625
        $constraintName = $this->normalizeIdentifier($constraintName);
430
431
        if (! $this->hasForeignKey($constraintName)) {
432
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
433
        }
434
435
        return $this->_fkConstraints[$constraintName];
436
    }
437
438 14737
    /**
439
     * Removes the foreign key constraint with the given name.
440 14737
     *
441
     * @param string $constraintName The constraint name.
442 14737
     *
443
     * @throws SchemaException
444
     */
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 14394
453
        unset($this->_fkConstraints[$constraintName]);
454 14394
    }
455
456 14394
    /**
457
     * Returns whether this table has a unique constraint with the given name.
458
     */
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 321
     *
469
     * @param string $constraintName The constraint name.
470 321
     *
471
     * @throws SchemaException If the foreign key does not exist.
472 321
     */
473
    public function getUniqueConstraint(string $constraintName) : UniqueConstraint
474
    {
475
        $constraintName = $this->normalizeIdentifier($constraintName);
476 321
477
        if (! $this->hasUniqueConstraint($constraintName)) {
478
            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 322
     *
489
     * @throws SchemaException
490 322
     */
491
    public function removeUniqueConstraint(string $constraintName) : void
492 322
    {
493
        $constraintName = $this->normalizeIdentifier($constraintName);
494
495
        if (! $this->hasUniqueConstraint($constraintName)) {
496 322
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
497 322
        }
498
499
        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 17411
        }
559
560 17411
        return $this->_columns[$columnName];
561 17411
    }
562 17411
563
    /**
564 17411
     * Returns the primary key.
565
     *
566 17411
     * @return Index|null The primary key, or null if this Table has no primary key.
567 17173
     */
568
    public function getPrimaryKey() : ?Index
569
    {
570 17411
        return $this->hasPrimaryKey()
571
            ? $this->getIndex($this->_primaryKeyName)
572 16658
            : null;
573
    }
574
575 17411
    /**
576
     * Returns the primary key columns.
577
     *
578 17250
     * @return string[]
579 17411
     *
580
     * @throws DBALException
581 17411
     */
582
    public function getPrimaryKeyColumns() : array
583
    {
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 17438
    }
592
593 17438
    /**
594
     * Returns whether this table has a primary key.
595 17438
     */
596
    public function hasPrimaryKey() : bool
597
    {
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 17334
    {
608
        $indexName = $this->normalizeIdentifier($indexName);
609 17334
610
        return isset($this->_indexes[$indexName]);
611 17334
    }
612 1417
613
    /**
614
     * Returns the Index with the given name.
615 17333
     *
616
     * @param string $indexName The index name.
617
     *
618
     * @throws SchemaException If the index does not exist.
619
     */
620
    public function getIndex(string $indexName) : Index
621
    {
622
        $indexName = $this->normalizeIdentifier($indexName);
623 17416
624
        if (! $this->hasIndex($indexName)) {
625 17416
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
626 17178
        }
627 17416
628
        return $this->_indexes[$indexName];
629
    }
630
631
    /**
632
     * @return Index[]
633
     */
634
    public function getIndexes() : array
635
    {
636
        return $this->_indexes;
637 14576
    }
638
639 14576
    /**
640
     * Returns the unique constraints.
641 14576
     *
642
     * @return UniqueConstraint[]
643
     */
644
    public function getUniqueConstraints() : array
645 14576
    {
646
        return $this->_uniqueConstraints;
647
    }
648
649
    /**
650
     * Returns the foreign key constraints.
651
     *
652
     * @return ForeignKeyConstraint[]
653 17419
     */
654
    public function getForeignKeys() : array
655 17419
    {
656
        return $this->_fkConstraints;
657
    }
658
659
    public function hasOption(string $name) : bool
660
    {
661
        return isset($this->_options[$name]);
662
    }
663
664
    /**
665 17230
     * @return mixed
666
     */
667 17230
    public function getOption(string $name)
668
    {
669 17230
        return $this->_options[$name];
670
    }
671
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 17208
    {
682
        $visitor->acceptTable($this);
683 17208
684
        foreach ($this->getColumns() as $column) {
685 17208
            $visitor->acceptColumn($this, $column);
686 1297
        }
687
688
        foreach ($this->getIndexes() as $index) {
689 17207
            $visitor->acceptIndex($this, $index);
690
        }
691
692
        foreach ($this->getForeignKeys() as $constraint) {
693
            $visitor->acceptForeignKey($this, $constraint);
694
        }
695 17398
    }
696
697 17398
    /**
698
     * Clone of a Table triggers a deep clone of all affected assets.
699
     */
700
    public function __clone()
701
    {
702
        foreach ($this->_columns as $k => $column) {
703
            $this->_columns[$k] = clone $column;
704
        }
705 17287
706
        foreach ($this->_indexes as $k => $index) {
707 17287
            $this->_indexes[$k] = clone $index;
708
        }
709
710
        foreach ($this->_fkConstraints as $k => $fk) {
711
            $this->_fkConstraints[$k] = clone $fk;
712
            $this->_fkConstraints[$k]->setLocalTable($this);
713
        }
714
    }
715 17429
716
    protected function _getMaxIdentifierLength() : int
717 17429
    {
718
        return $this->_schemaConfig instanceof SchemaConfig
719
            ? $this->_schemaConfig->getMaxIdentifierLength()
720
            : 63;
721
    }
722
723
    /**
724
     * @throws SchemaException
725 14676
     */
726
    protected function _addColumn(Column $column) : void
727 14676
    {
728
        $columnName = $column->getName();
729
        $columnName = $this->normalizeIdentifier($columnName);
730
731
        if (isset($this->_columns[$columnName])) {
732
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
733
        }
734
735 14240
        $this->_columns[$columnName] = $column;
736
    }
737 14240
738
    /**
739
     * Adds an index to the table.
740
     *
741
     * @throws SchemaException
742
     */
743 17301
    protected function _addIndex(Index $indexCandidate) : self
744
    {
745 17301
        $indexName               = $indexCandidate->getName();
746
        $indexName               = $this->normalizeIdentifier($indexName);
747
        $replacedImplicitIndexes = [];
748
749
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
750
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
751 16453
                continue;
752
            }
753 16453
754
            $replacedImplicitIndexes[] = $name;
755 16453
        }
756 16450
757
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
758
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
759 16453
        ) {
760 14908
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
761
        }
762
763 16453
        foreach ($replacedImplicitIndexes as $name) {
764 148
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
765
        }
766 16453
767
        if ($indexCandidate->isPrimary()) {
768
            $this->_primaryKeyName = $indexName;
769
        }
770
771
        $this->_indexes[$indexName] = $indexCandidate;
772
773 15841
        return $this;
774
    }
775 15841
776 15840
    protected function _addUniqueConstraint(UniqueConstraint $constraint) : self
777
    {
778
        $name = $constraint->getName() ?? $this->_generateIdentifierName(
779 15841
            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 15829
            'fk',
781
            $this->_getMaxIdentifierLength()
782
        );
783 15841
784 14872
        $name = $this->normalizeIdentifier($name);
785 14872
786
        $this->_uniqueConstraints[$name] = $constraint;
787 15841
788
        // If there is already an index that fulfills this requirements drop the request. In the case of __construct
789
        // 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 16791
            array_merge([$this->getName()], $constraint->getColumns()),
793
            'idx',
794 16791
            $this->_getMaxIdentifierLength()
795 16530
        );
796 16791
797
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false);
798
799
        foreach ($this->_indexes as $existingIndex) {
800
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
801
                return $this;
802
            }
803
        }
804 17563
805
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
806 17563
807 17563
        return $this;
808
    }
809 17563
810 1393
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) : self
811
    {
812
        $constraint->setLocalTable($this);
813 17563
814 17563
        $name = $constraint->getName() ?? $this->_generateIdentifierName(
815
            array_merge([$this->getName()], $constraint->getLocalColumns()),
816
            'fk',
817
            $this->_getMaxIdentifierLength()
818
        );
819
820
        $name = $this->normalizeIdentifier($name);
821
822
        $this->_fkConstraints[$name] = $constraint;
823 17378
824
        // add an explicit index on the foreign key columns.
825 17378
        // If there is already an index that fulfills this requirements drop the request. In the case of __construct
826 17378
        // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates.
827 17378
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
828
        $indexName = $this->_generateIdentifierName(
829 17378
            array_merge([$this->getName()], $constraint->getColumns()),
830 13311
            'idx',
831 13307
            $this->_getMaxIdentifierLength()
832
        );
833
834 724
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
835
836
        foreach ($this->_indexes as $existingIndex) {
837 17378
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
838 17378
                return $this;
839
            }
840 1274
        }
841
842
        $this->_addIndex($indexCandidate);
843 17378
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
844 724
845
        return $this;
846
    }
847 17378
848 17256
    /**
849
     * Normalizes a given identifier.
850
     *
851 17378
     * Trims quotes and lowercases the given identifier.
852
     *
853 17378
     * @param string|null $identifier The identifier to normalize.
854
     *
855
     * @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 16708
     *
899
     * @throws SchemaException
900 16708
     */
901
    private function _createIndex(array $columns, string $indexName, bool $isUnique, bool $isPrimary, array $flags = [], array $options = []) : Index
902 16708
    {
903 16629
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
904 1795
            throw SchemaException::indexNameInvalid($indexName);
905 1795
        }
906 1795
907 16708
        foreach ($columns as $index => $value) {
908
            if (is_string($index)) {
909
                $columnName = $index;
910 16708
            } else {
911
                $columnName = $value;
912 16708
            }
913
914
            if (! $this->hasColumn($columnName)) {
915
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
916
            }
917
        }
918 16708
919 16708
        return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
920 16708
    }
921
}
922