Failed Conditions
Pull Request — develop (#3348)
by Sergei
22:47
created

Table::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
ccs 4
cts 4
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 17618
    public function __construct(
61
        string $tableName,
62
        array $columns = [],
63
        array $indexes = [],
64
        array $uniqueConstraints = [],
65
        array $fkConstraints = [],
66
        array $options = []
67
    ) {
68 17618
        if (strlen($tableName) === 0) {
69 1563
            throw DBALException::invalidTableName($tableName);
70
        }
71
72 17617
        $this->_setName($tableName);
73
74 17617
        foreach ($columns as $column) {
75 16156
            $this->_addColumn($column);
76
        }
77
78 17616
        foreach ($indexes as $idx) {
79 16083
            $this->_addIndex($idx);
80
        }
81
82 17614
        foreach ($uniqueConstraints as $uniqueConstraint) {
83
            $this->_addUniqueConstraint($uniqueConstraint);
84
        }
85
86 17614
        foreach ($fkConstraints as $fkConstraint) {
87 15317
            $this->_addForeignKeyConstraint($fkConstraint);
88
        }
89
90 17614
        $this->_options = $options;
91 17614
    }
92
93 17593
    public function getName() : string
94
    {
95 17593
        $name = parent::getName();
96 17593
        assert(is_string($name));
97
98 17593
        return $name;
99
    }
100
101 16637
    public function setSchemaConfig(SchemaConfig $schemaConfig) : void
102
    {
103 16637
        $this->_schemaConfig = $schemaConfig;
104 16637
    }
105
106
    /**
107
     * Sets the Primary Key.
108
     *
109
     * @param string[]     $columnNames
110
     * @param string|false $indexName
111
     */
112 17218
    public function setPrimaryKey(array $columnNames, $indexName = false) : self
113
    {
114 17218
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
115
116 17218
        foreach ($columnNames as $columnName) {
117 17218
            $column = $this->getColumn($columnName);
118 17218
            $column->setNotnull(true);
119
        }
120
121 17218
        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 15686
    public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
148
    {
149 15686
        if ($indexName === null) {
150 15282
            $indexName = $this->_generateIdentifierName(
151 15282
                array_merge([$this->getName()], $columnNames),
152 15282
                'idx',
153 15282
                $this->_getMaxIdentifierLength()
154
            );
155
        }
156
157 15686
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
158
    }
159
160
    /**
161
     * Drops the primary key from this table.
162
     */
163 14545
    public function dropPrimaryKey() : void
164
    {
165 14545
        $this->dropIndex($this->_primaryKeyName);
166 14545
        $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 14545
    }
168
169
    /**
170
     * 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 14568
    public function dropIndex(string $indexName) : void
177
    {
178 14568
        $indexName = $this->normalizeIdentifier($indexName);
179
180 14568
        if (! $this->hasIndex($indexName)) {
181
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
182
        }
183
184 14568
        unset($this->_indexes[$indexName]);
185 14568
    }
186
187
    /**
188
     * @param string[] $columnNames
189
     * @param mixed[]  $options
190
     */
191 16522
    public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []) : self
192
    {
193 16522
        if ($indexName === null) {
194 16189
            $indexName = $this->_generateIdentifierName(
195 16189
                array_merge([$this->getName()], $columnNames),
196 16189
                'uniq',
197 16189
                $this->_getMaxIdentifierLength()
198
            );
199
        }
200
201 16522
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
202
    }
203
204
    /**
205
     * 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
     *                                  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 14369
    public function renameIndex(string $oldIndexName, ?string $newIndexName = null) : self
217
    {
218 14369
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
219 14369
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
220
221 14369
        if ($oldIndexName === $normalizedNewIndexName) {
222 443
            return $this;
223
        }
224
225 14369
        if (! $this->hasIndex($oldIndexName)) {
226 363
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
227
        }
228
229 14368
        if ($this->hasIndex($normalizedNewIndexName)) {
230 339
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
231
        }
232
233 14367
        $oldIndex = $this->_indexes[$oldIndexName];
234
235 14367
        if ($oldIndex->isPrimary()) {
236 435
            $this->dropPrimaryKey();
237
238 435
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
239
        }
240
241 14367
        unset($this->_indexes[$oldIndexName]);
242
243 14367
        if ($oldIndex->isUnique()) {
244 436
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
245
        }
246
247 14366
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
248
    }
249
250
    /**
251
     * Checks if an index begins in the order of the given columns.
252
     *
253
     * @param string[] $columnNames
254
     */
255 14549
    public function columnsAreIndexed(array $columnNames) : bool
256
    {
257 14549
        foreach ($this->getIndexes() as $index) {
258
            /** @var $index Index */
259 14549
            if ($index->spansColumns($columnNames)) {
260 14549
                return true;
261
            }
262
        }
263
264
        return false;
265
    }
266
267
    /**
268
     * @param mixed[] $options
269
     */
270 17498
    public function addColumn(string $columnName, string $typeName, array $options = []) : Column
271
    {
272 17498
        $column = new Column($columnName, Type::getType($typeName), $options);
273
274 17498
        $this->_addColumn($column);
275
276 17498
        return $column;
277
    }
278
279
    /**
280
     * Renames a Column.
281
     *
282
     * @deprecated
283
     *
284
     * @throws DBALException
285
     */
286
    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
        throw new DBALException(
289
            'Table#renameColumn() was removed, because it drops and recreates the column instead. ' .
290
            '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 14802
    public function changeColumn(string $columnName, array $options) : self
301
    {
302 14802
        $column = $this->getColumn($columnName);
303
304 14802
        $column->setOptions($options);
305
306 14802
        return $this;
307
    }
308
309
    /**
310
     * Drops a Column from the Table.
311
     */
312 1451
    public function dropColumn(string $columnName) : self
313
    {
314 1451
        $columnName = $this->normalizeIdentifier($columnName);
315
316 1451
        unset($this->_columns[$columnName]);
317
318 1451
        return $this;
319
    }
320
321
    /**
322
     * Adds a foreign key constraint.
323
     *
324
     * Name is inferred from the local columns.
325
     *
326
     * @param Table|string $foreignTable       Table schema instance or table name
327
     * @param string[]     $localColumnNames
328
     * @param string[]     $foreignColumnNames
329
     * @param mixed[]      $options
330
     */
331 16589
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], ?string $constraintName = null) : self
332
    {
333 16589
        if (! $constraintName) {
334 16543
            $constraintName = $this->_generateIdentifierName(
335 16543
                array_merge((array) $this->getName(), $localColumnNames),
336 16543
                'fk',
337 16543
                $this->_getMaxIdentifierLength()
338
            );
339
        }
340
341 16589
        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
    }
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 10318
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = []) : self
357
    {
358 10318
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
359
    }
360
361
    /**
362
     * Adds a foreign key constraint with a given name.
363
     *
364
     * @deprecated Use {@link addForeignKeyConstraint}
365
     *
366
     * @param Table|string $foreignTable       Table schema instance or table name
367
     * @param string[]     $localColumnNames
368
     * @param string[]     $foreignColumnNames
369
     * @param mixed[]      $options
370
     *
371
     * @throws SchemaException
372
     */
373 16590
    public function addNamedForeignKeyConstraint(string $name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = []) : self
374
    {
375 16590
        if ($foreignTable instanceof Table) {
376 16182
            foreach ($foreignColumnNames as $columnName) {
377 16182
                if (! $foreignTable->hasColumn($columnName)) {
378 1056
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
379
                }
380
            }
381
        }
382
383 16589
        foreach ($localColumnNames as $columnName) {
384 16589
            if (! $this->hasColumn($columnName)) {
385 1124
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
386
            }
387
        }
388
389 16588
        $constraint = new ForeignKeyConstraint(
390 15959
            $localColumnNames,
391 89
            $foreignTable,
392 89
            $foreignColumnNames,
393 89
            $name,
394 718
            $options
395
        );
396
397 16588
        return $this->_addForeignKeyConstraint($constraint);
398
    }
399
400
    /**
401
     * @param mixed $value
402
     */
403 14702
    public function addOption(string $name, $value) : self
404
    {
405 14702
        $this->_options[$name] = $value;
406
407 14702
        return $this;
408
    }
409
410
    /**
411
     * Returns whether this table has a foreign key constraint with the given name.
412
     */
413 14362
    public function hasForeignKey(string $constraintName) : bool
414
    {
415 14362
        $constraintName = $this->normalizeIdentifier($constraintName);
416
417 14362
        return isset($this->_fkConstraints[$constraintName]);
418
    }
419
420
    /**
421
     * Returns the foreign key constraint with the given name.
422
     *
423
     * @param string $constraintName The constraint name.
424
     *
425
     * @throws SchemaException If the foreign key does not exist.
426
     */
427 323
    public function getForeignKey(string $constraintName) : ForeignKeyConstraint
428
    {
429 323
        $constraintName = $this->normalizeIdentifier($constraintName);
430
431 323
        if (! $this->hasForeignKey($constraintName)) {
432
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
433
        }
434
435 323
        return $this->_fkConstraints[$constraintName];
436
    }
437
438
    /**
439
     * Removes the foreign key constraint with the given name.
440
     *
441
     * @param string $constraintName The constraint name.
442
     *
443
     * @throws SchemaException
444
     */
445 324
    public function removeForeignKey(string $constraintName) : void
446
    {
447 324
        $constraintName = $this->normalizeIdentifier($constraintName);
448
449 324
        if (! $this->hasForeignKey($constraintName)) {
450
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
451
        }
452
453 324
        unset($this->_fkConstraints[$constraintName]);
454 324
    }
455
456
    /**
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
     *
469
     * @param string $constraintName The constraint name.
470
     *
471
     * @throws SchemaException If the foreign key does not exist.
472
     */
473
    public function getUniqueConstraint(string $constraintName) : UniqueConstraint
474
    {
475
        $constraintName = $this->normalizeIdentifier($constraintName);
476
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
     *
489
     * @throws SchemaException
490
     */
491
    public function removeUniqueConstraint(string $constraintName) : void
492
    {
493
        $constraintName = $this->normalizeIdentifier($constraintName);
494
495
        if (! $this->hasUniqueConstraint($constraintName)) {
496
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
497
        }
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 17375
    public function getColumns() : array
508
    {
509 17375
        $columns = $this->_columns;
510 17375
        $pkCols  = [];
511 17375
        $fkCols  = [];
512
513 17375
        $primaryKey = $this->getPrimaryKey();
514
515 17375
        if ($primaryKey !== null) {
516 17137
            $pkCols = $primaryKey->getColumns();
517
        }
518
519 17375
        foreach ($this->getForeignKeys() as $fk) {
520
            /** @var ForeignKeyConstraint $fk */
521 16622
            $fkCols = array_merge($fkCols, $fk->getColumns());
522
        }
523
524 17375
        $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
525
526
        uksort($columns, static function ($a, $b) use ($colNames) : bool {
527 17214
            return array_search($a, $colNames) >= array_search($b, $colNames);
528 17375
        });
529
530 17375
        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 17402
    public function hasColumn(string $columnName) : bool
539
    {
540 17402
        $columnName = $this->normalizeIdentifier($columnName);
541
542 17402
        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 17298
    public function getColumn(string $columnName) : Column
553
    {
554 17298
        $columnName = $this->normalizeIdentifier($columnName);
555
556 17298
        if (! $this->hasColumn($columnName)) {
557 1419
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
558
        }
559
560 17297
        return $this->_columns[$columnName];
561
    }
562
563
    /**
564
     * Returns the primary key.
565
     *
566
     * @return Index|null The primary key, or null if this Table has no primary key.
567
     */
568 17380
    public function getPrimaryKey() : ?Index
569
    {
570 17380
        return $this->hasPrimaryKey()
571 17142
            ? $this->getIndex($this->_primaryKeyName)
572 17380
            : null;
573
    }
574
575
    /**
576
     * Returns the primary key columns.
577
     *
578
     * @return string[]
579
     *
580
     * @throws DBALException
581
     */
582 14540
    public function getPrimaryKeyColumns() : array
583
    {
584 14540
        $primaryKey = $this->getPrimaryKey();
585
586 14540
        if ($primaryKey === null) {
587
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
588
        }
589
590 14540
        return $primaryKey->getColumns();
591
    }
592
593
    /**
594
     * Returns whether this table has a primary key.
595
     */
596 17383
    public function hasPrimaryKey() : bool
597
    {
598 17383
        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 17194
    public function hasIndex(string $indexName) : bool
607
    {
608 17194
        $indexName = $this->normalizeIdentifier($indexName);
609
610 17194
        return isset($this->_indexes[$indexName]);
611
    }
612
613
    /**
614
     * Returns the Index with the given name.
615
     *
616
     * @param string $indexName The index name.
617
     *
618
     * @throws SchemaException If the index does not exist.
619
     */
620 17172
    public function getIndex(string $indexName) : Index
621
    {
622 17172
        $indexName = $this->normalizeIdentifier($indexName);
623
624 17172
        if (! $this->hasIndex($indexName)) {
625 1299
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
626
        }
627
628 17171
        return $this->_indexes[$indexName];
629
    }
630
631
    /**
632
     * @return Index[]
633
     */
634 17362
    public function getIndexes() : array
635
    {
636 17362
        return $this->_indexes;
637
    }
638
639
    /**
640
     * Returns the unique constraints.
641
     *
642
     * @return UniqueConstraint[]
643
     */
644 17251
    public function getUniqueConstraints() : array
645
    {
646 17251
        return $this->_uniqueConstraints;
647
    }
648
649
    /**
650
     * Returns the foreign key constraints.
651
     *
652
     * @return ForeignKeyConstraint[]
653
     */
654 17393
    public function getForeignKeys() : array
655
    {
656 17393
        return $this->_fkConstraints;
657
    }
658
659 14640
    public function hasOption(string $name) : bool
660
    {
661 14640
        return isset($this->_options[$name]);
662
    }
663
664
    /**
665
     * @return mixed
666
     */
667 14202
    public function getOption(string $name)
668
    {
669 14202
        return $this->_options[$name];
670
    }
671
672
    /**
673
     * @return mixed[]
674
     */
675 17265
    public function getOptions() : array
676
    {
677 17265
        return $this->_options;
678
    }
679
680 16416
    public function visit(Visitor $visitor) : void
681
    {
682 16416
        $visitor->acceptTable($this);
683
684 16416
        foreach ($this->getColumns() as $column) {
685 16413
            $visitor->acceptColumn($this, $column);
686
        }
687
688 16416
        foreach ($this->getIndexes() as $index) {
689 14868
            $visitor->acceptIndex($this, $index);
690
        }
691
692 16416
        foreach ($this->getForeignKeys() as $constraint) {
693 148
            $visitor->acceptForeignKey($this, $constraint);
694
        }
695 16416
    }
696
697
    /**
698
     * Clone of a Table triggers a deep clone of all affected assets.
699
     */
700 15805
    public function __clone()
701
    {
702 15805
        foreach ($this->_columns as $k => $column) {
703 15804
            $this->_columns[$k] = clone $column;
704
        }
705
706 15805
        foreach ($this->_indexes as $k => $index) {
707 15793
            $this->_indexes[$k] = clone $index;
708
        }
709
710 15805
        foreach ($this->_fkConstraints as $k => $fk) {
711 14840
            $this->_fkConstraints[$k] = clone $fk;
712 14840
            $this->_fkConstraints[$k]->setLocalTable($this);
713
        }
714 15805
    }
715
716 16754
    protected function _getMaxIdentifierLength() : int
717
    {
718 16754
        return $this->_schemaConfig instanceof SchemaConfig
719 16493
            ? $this->_schemaConfig->getMaxIdentifierLength()
720 16754
            : 63;
721
    }
722
723
    /**
724
     * @throws SchemaException
725
     */
726 17527
    protected function _addColumn(Column $column) : void
727
    {
728 17527
        $columnName = $column->getName();
729 17527
        $columnName = $this->normalizeIdentifier($columnName);
730
731 17527
        if (isset($this->_columns[$columnName])) {
732 1395
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
733
        }
734
735 17527
        $this->_columns[$columnName] = $column;
736 17527
    }
737
738
    /**
739
     * Adds an index to the table.
740
     *
741
     * @throws SchemaException
742
     */
743 17342
    protected function _addIndex(Index $indexCandidate) : self
744
    {
745 17342
        $indexName               = $indexCandidate->getName();
746 17342
        $indexName               = $this->normalizeIdentifier($indexName);
747 17342
        $replacedImplicitIndexes = [];
748
749 17342
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
750 13313
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
751 13309
                continue;
752
            }
753
754 726
            $replacedImplicitIndexes[] = $name;
755
        }
756
757 17342
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
758 17342
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
759
        ) {
760 1276
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
761
        }
762
763 17342
        foreach ($replacedImplicitIndexes as $name) {
764 726
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
765
        }
766
767 17342
        if ($indexCandidate->isPrimary()) {
768 17220
            $this->_primaryKeyName = $indexName;
769
        }
770
771 17342
        $this->_indexes[$indexName] = $indexCandidate;
772
773 17342
        return $this;
774
    }
775
776
    protected function _addUniqueConstraint(UniqueConstraint $constraint) : self
777
    {
778
        $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
            $this->_getMaxIdentifierLength()
782
        );
783
784
        $name = $this->normalizeIdentifier($name);
785
786
        $this->_uniqueConstraints[$name] = $constraint;
787
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
            array_merge([$this->getName()], $constraint->getColumns()),
793
            'idx',
794
            $this->_getMaxIdentifierLength()
795
        );
796
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
805
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
806
807
        return $this;
808
    }
809
810 16672
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) : self
811
    {
812 16672
        $constraint->setLocalTable($this);
813
814 16672
        $name = $constraint->getName() ?? $this->_generateIdentifierName(
815 1798
            array_merge([$this->getName()], $constraint->getLocalColumns()),
816 1798
            'fk',
817 16672
            $this->_getMaxIdentifierLength()
818
        );
819
820 16672
        $name = $this->normalizeIdentifier($name);
821
822 16672
        $this->_fkConstraints[$name] = $constraint;
823
824
        // add an explicit index on the foreign key columns.
825
        // 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
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
828 16672
        $indexName = $this->_generateIdentifierName(
829 16672
            array_merge([$this->getName()], $constraint->getColumns()),
830 16672
            'idx',
831 16672
            $this->_getMaxIdentifierLength()
832
        );
833
834 16672
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
835
836 16672
        foreach ($this->_indexes as $existingIndex) {
837 16646
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
838 15294
                return $this;
839
            }
840
        }
841
842 16648
        $this->_addIndex($indexCandidate);
843 16648
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
844
845 16648
        return $this;
846
    }
847
848
    /**
849
     * Normalizes a given identifier.
850
     *
851
     * Trims quotes and lowercases the given identifier.
852
     *
853
     * @param string|null $identifier The identifier to normalize.
854
     *
855
     * @return string The normalized identifier.
856
     */
857 17531
    private function normalizeIdentifier(?string $identifier) : string
858
    {
859 17531
        if ($identifier === null) {
860 435
            return '';
861
        }
862
863 17531
        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
     */
901 17337
    private function _createIndex(array $columns, string $indexName, bool $isUnique, bool $isPrimary, array $flags = [], array $options = []) : Index
902
    {
903 17337
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
904 1107
            throw SchemaException::indexNameInvalid($indexName);
905
        }
906
907 17336
        foreach ($columns as $index => $value) {
908 17335
            if (is_string($index)) {
909
                $columnName = $index;
910
            } else {
911 17335
                $columnName = $value;
912
            }
913
914 17335
            if (! $this->hasColumn($columnName)) {
915 1478
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
916
            }
917
        }
918
919 17335
        return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
920
    }
921
}
922