Failed Conditions
Pull Request — develop (#3348)
by Sergei
65:06
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
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\Visitor\Visitor;
7
use Doctrine\DBAL\Types\Type;
8
use function array_keys;
9
use function array_merge;
10
use function array_search;
11
use function array_unique;
12
use function assert;
13
use function in_array;
14
use function is_string;
15
use function preg_match;
16
use function strlen;
17
use function strtolower;
18
use function uksort;
19
20
/**
21
 * Object Representation of a table.
22
 */
23
class Table extends AbstractAsset
24
{
25
    /** @var Column[] */
26
    protected $_columns = [];
27
28
    /** @var Index[] */
29
    private $implicitIndexes = [];
30
31
    /** @var Index[] */
32
    protected $_indexes = [];
33
34
    /** @var string */
35
    protected $_primaryKeyName = false;
36
37
    /** @var UniqueConstraint[] */
38
    protected $_uniqueConstraints = [];
39
40
    /** @var ForeignKeyConstraint[] */
41
    protected $_fkConstraints = [];
42
43
    /** @var mixed[] */
44
    protected $_options = [];
45
46
    /** @var SchemaConfig|null */
47
    protected $_schemaConfig = null;
48
49
    /**
50
     * @param Column[]               $columns
51
     * @param Index[]                $indexes
52
     * @param UniqueConstraint[]     $uniqueConstraints
53
     * @param ForeignKeyConstraint[] $fkConstraints
54
     * @param mixed[]                $options
55
     *
56
     * @throws DBALException
57
     */
58 17654
    public function __construct(
59
        string $tableName,
60
        array $columns = [],
61
        array $indexes = [],
62
        array $uniqueConstraints = [],
63
        array $fkConstraints = [],
64
        array $options = []
65
    ) {
66 17654
        if (strlen($tableName) === 0) {
67 1561
            throw DBALException::invalidTableName($tableName);
68
        }
69
70 17653
        $this->_setName($tableName);
71
72 17653
        foreach ($columns as $column) {
73 16192
            $this->_addColumn($column);
74
        }
75
76 17652
        foreach ($indexes as $idx) {
77 16119
            $this->_addIndex($idx);
78
        }
79
80 17650
        foreach ($uniqueConstraints as $uniqueConstraint) {
81
            $this->_addUniqueConstraint($uniqueConstraint);
82
        }
83
84 17650
        foreach ($fkConstraints as $fkConstraint) {
85 15348
            $this->_addForeignKeyConstraint($fkConstraint);
86
        }
87
88 17650
        $this->_options = $options;
89 17650
    }
90
91
    public function getName() : string
92
    {
93
        $name = parent::getName();
94 16674
        assert(is_string($name));
95
96 16674
        return $name;
97 16674
    }
98
99
    public function setSchemaConfig(SchemaConfig $schemaConfig) : void
100
    {
101
        $this->_schemaConfig = $schemaConfig;
102
    }
103
104
    /**
105
     * Sets the Primary Key.
106
     *
107 17254
     * @param string[]     $columnNames
108
     * @param string|false $indexName
109 17254
     */
110
    public function setPrimaryKey(array $columnNames, $indexName = false) : self
111 17254
    {
112 17254
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
113 17254
114
        foreach ($columnNames as $columnName) {
115
            $column = $this->getColumn($columnName);
116 17254
            $column->setNotnull(true);
117
        }
118
119
        return $this;
120
    }
121
122
    /**
123
     * @param mixed[]  $columnNames
124
     * @param string[] $flags
125
     * @param mixed[]  $options
126
     */
127
    public function addUniqueConstraint(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
128
    {
129
        if ($indexName === null) {
130
            $indexName = $this->_generateIdentifierName(
131
                array_merge([$this->getName()], $columnNames),
132
                'uniq',
133
                $this->_getMaxIdentifierLength()
134
            );
135
        }
136
137
        return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options));
138
    }
139
140
    /**
141
     * @param string[] $columnNames
142
     * @param string[] $flags
143
     * @param mixed[]  $options
144
     */
145
    public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
146
    {
147
        if ($indexName === null) {
148 15724
            $indexName = $this->_generateIdentifierName(
149
                array_merge([$this->getName()], $columnNames),
150 15724
                'idx',
151 15322
                $this->_getMaxIdentifierLength()
152 15322
            );
153 15322
        }
154 15322
155
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
156
    }
157
158 15724
    /**
159
     * Drops the primary key from this table.
160
     */
161
    public function dropPrimaryKey() : void
162
    {
163
        $this->dropIndex($this->_primaryKeyName);
164
        $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...
165
    }
166 14581
167
    /**
168 14581
     * Drops an index from this table.
169 14581
     *
170 14581
     * @param string $indexName The index name.
171
     *
172
     * @throws SchemaException If the index does not exist.
173
     */
174
    public function dropIndex(string $indexName) : void
175
    {
176
        $indexName = $this->normalizeIdentifier($indexName);
177
178
        if (! $this->hasIndex($indexName)) {
179
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
180
        }
181 14604
182
        unset($this->_indexes[$indexName]);
183 14604
    }
184
185 14604
    /**
186
     * @param string[] $columnNames
187
     * @param mixed[]  $options
188
     */
189 14604
    public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []) : self
190 14604
    {
191
        if ($indexName === null) {
192
            $indexName = $this->_generateIdentifierName(
193
                array_merge([$this->getName()], $columnNames),
194
                'uniq',
195
                $this->_getMaxIdentifierLength()
196
            );
197
        }
198
199 16559
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
200
    }
201 16559
202 16227
    /**
203 16227
     * Renames an index.
204 16227
     *
205 16227
     * @param string      $oldIndexName The name of the index to rename from.
206
     * @param string|null $newIndexName The name of the index to rename to.
207
     *                                  If null is given, the index name will be auto-generated.
208
     *
209 16559
     * @return self This table instance.
210
     *
211
     * @throws SchemaException If no index exists for the given current name
212
     *                         or if an index with the given new name already exists on this table.
213
     */
214
    public function renameIndex(string $oldIndexName, ?string $newIndexName = null) : self
215
    {
216
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
217
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
218
219
        if ($oldIndexName === $normalizedNewIndexName) {
220
            return $this;
221
        }
222
223
        if (! $this->hasIndex($oldIndexName)) {
224 14401
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
225
        }
226 14401
227 14401
        if ($this->hasIndex($normalizedNewIndexName)) {
228
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
229 14401
        }
230 441
231
        $oldIndex = $this->_indexes[$oldIndexName];
232
233 14401
        if ($oldIndex->isPrimary()) {
234 361
            $this->dropPrimaryKey();
235
236
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
237 14400
        }
238 337
239
        unset($this->_indexes[$oldIndexName]);
240
241 14399
        if ($oldIndex->isUnique()) {
242
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
243 14399
        }
244 433
245
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
246 433
    }
247
248
    /**
249 14399
     * Checks if an index begins in the order of the given columns.
250
     *
251 14399
     * @param string[] $columnNames
252 434
     */
253
    public function columnsAreIndexed(array $columnNames) : bool
254
    {
255 14398
        foreach ($this->getIndexes() as $index) {
256
            /** @var $index Index */
257
            if ($index->spansColumns($columnNames)) {
258
                return true;
259
            }
260
        }
261
262
        return false;
263
    }
264
265 14581
    /**
266
     * @param mixed[] $options
267 14581
     */
268
    public function addColumn(string $columnName, string $typeName, array $options = []) : Column
269 14581
    {
270 14581
        $column = new Column($columnName, Type::getType($typeName), $options);
271
272
        $this->_addColumn($column);
273
274
        return $column;
275
    }
276
277
    /**
278
     * Renames a Column.
279
     *
280
     * @deprecated
281
     *
282
     * @throws DBALException
283
     */
284 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

284
    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

284
    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...
285
    {
286 17534
        throw new DBALException(
287
            'Table#renameColumn() was removed, because it drops and recreates the column instead. ' .
288 17534
            'There is no fix available, because a schema diff cannot reliably detect if a column ' .
289
            'was renamed or one column was created and another one dropped.'
290 17534
        );
291
    }
292
293
    /**
294
     * Change Column Details.
295
     *
296
     * @param mixed[] $options
297
     */
298
    public function changeColumn(string $columnName, array $options) : self
299
    {
300
        $column = $this->getColumn($columnName);
301
302
        $column->setOptions($options);
303
304
        return $this;
305
    }
306
307
    /**
308
     * Drops a Column from the Table.
309
     */
310
    public function dropColumn(string $columnName) : self
311
    {
312
        $columnName = $this->normalizeIdentifier($columnName);
313
314
        unset($this->_columns[$columnName]);
315
316
        return $this;
317
    }
318
319
    /**
320 14833
     * Adds a foreign key constraint.
321
     *
322 14833
     * Name is inferred from the local columns.
323
     *
324 14833
     * @param Table|string $foreignTable       Table schema instance or table name
325
     * @param string[]     $localColumnNames
326 14833
     * @param string[]     $foreignColumnNames
327
     * @param mixed[]      $options
328
     * @param string|null  $constraintName
329
     */
330
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null) : self
331
    {
332
        if (! $constraintName) {
333
            $constraintName = $this->_generateIdentifierName(
334
                array_merge((array) $this->getName(), $localColumnNames),
335
                'fk',
336 1449
                $this->_getMaxIdentifierLength()
337
            );
338 1449
        }
339
340 1449
        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

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

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