Failed Conditions
Pull Request — develop (#3348)
by Sergei
21:04
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 17631
    public function __construct(
61
        string $tableName,
62
        array $columns = [],
63
        array $indexes = [],
64
        array $uniqueConstraints = [],
65
        array $fkConstraints = [],
66
        array $options = []
67
    ) {
68 17631
        if (strlen($tableName) === 0) {
69 1561
            throw DBALException::invalidTableName($tableName);
70
        }
71
72 17630
        $this->_setName($tableName);
73
74 17630
        foreach ($columns as $column) {
75 16170
            $this->_addColumn($column);
76
        }
77
78 17629
        foreach ($indexes as $idx) {
79 16097
            $this->_addIndex($idx);
80
        }
81
82 17627
        foreach ($uniqueConstraints as $uniqueConstraint) {
83
            $this->_addUniqueConstraint($uniqueConstraint);
84
        }
85
86 17627
        foreach ($fkConstraints as $fkConstraint) {
87 15331
            $this->_addForeignKeyConstraint($fkConstraint);
88
        }
89
90 17627
        $this->_options = $options;
91 17627
    }
92
93 17606
    public function getName() : string
94
    {
95 17606
        $name = parent::getName();
96 17606
        assert(is_string($name));
97
98 17606
        return $name;
99
    }
100
101 16651
    public function setSchemaConfig(SchemaConfig $schemaConfig) : void
102
    {
103 16651
        $this->_schemaConfig = $schemaConfig;
104 16651
    }
105
106
    /**
107
     * Sets the Primary Key.
108
     *
109
     * @param string[]     $columnNames
110
     * @param string|false $indexName
111
     */
112 17231
    public function setPrimaryKey(array $columnNames, $indexName = false) : self
113
    {
114 17231
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
115
116 17231
        foreach ($columnNames as $columnName) {
117 17231
            $column = $this->getColumn($columnName);
118 17231
            $column->setNotnull(true);
119
        }
120
121 17231
        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 15703
    public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
148
    {
149 15703
        if ($indexName === null) {
150 15298
            $indexName = $this->_generateIdentifierName(
151 15298
                array_merge([$this->getName()], $columnNames),
152 15298
                'idx',
153 15298
                $this->_getMaxIdentifierLength()
154
            );
155
        }
156
157 15703
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
158
    }
159
160
    /**
161
     * Drops the primary key from this table.
162
     */
163 14561
    public function dropPrimaryKey() : void
164
    {
165 14561
        $this->dropIndex($this->_primaryKeyName);
166 14561
        $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 14561
    }
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 14584
    public function dropIndex(string $indexName) : void
177
    {
178 14584
        $indexName = $this->normalizeIdentifier($indexName);
179
180 14584
        if (! $this->hasIndex($indexName)) {
181
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
182
        }
183
184 14584
        unset($this->_indexes[$indexName]);
185 14584
    }
186
187
    /**
188
     * @param string[] $columnNames
189
     * @param mixed[]  $options
190
     */
191 16536
    public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []) : self
192
    {
193 16536
        if ($indexName === null) {
194 16202
            $indexName = $this->_generateIdentifierName(
195 16202
                array_merge([$this->getName()], $columnNames),
196 16202
                'uniq',
197 16202
                $this->_getMaxIdentifierLength()
198
            );
199
        }
200
201 16536
        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 14383
    public function renameIndex(string $oldIndexName, ?string $newIndexName = null) : self
217
    {
218 14383
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
219 14383
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
220
221 14383
        if ($oldIndexName === $normalizedNewIndexName) {
222 441
            return $this;
223
        }
224
225 14383
        if (! $this->hasIndex($oldIndexName)) {
226 361
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
227
        }
228
229 14382
        if ($this->hasIndex($normalizedNewIndexName)) {
230 337
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
231
        }
232
233 14381
        $oldIndex = $this->_indexes[$oldIndexName];
234
235 14381
        if ($oldIndex->isPrimary()) {
236 433
            $this->dropPrimaryKey();
237
238 433
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
239
        }
240
241 14381
        unset($this->_indexes[$oldIndexName]);
242
243 14381
        if ($oldIndex->isUnique()) {
244 434
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
245
        }
246
247 14380
        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 14563
    public function columnsAreIndexed(array $columnNames) : bool
256
    {
257 14563
        foreach ($this->getIndexes() as $index) {
258
            /** @var $index Index */
259 14563
            if ($index->spansColumns($columnNames)) {
260 14563
                return true;
261
            }
262
        }
263
264
        return false;
265
    }
266
267
    /**
268
     * @param mixed[] $options
269
     */
270 17511
    public function addColumn(string $columnName, string $typeName, array $options = []) : Column
271
    {
272 17511
        $column = new Column($columnName, Type::getType($typeName), $options);
273
274 17511
        $this->_addColumn($column);
275
276 17511
        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 14816
    public function changeColumn(string $columnName, array $options) : self
301
    {
302 14816
        $column = $this->getColumn($columnName);
303
304 14816
        $column->setOptions($options);
305
306 14816
        return $this;
307
    }
308
309
    /**
310
     * Drops a Column from the Table.
311
     */
312 1449
    public function dropColumn(string $columnName) : self
313
    {
314 1449
        $columnName = $this->normalizeIdentifier($columnName);
315
316 1449
        unset($this->_columns[$columnName]);
317
318 1449
        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 16603
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], ?string $constraintName = null) : self
332
    {
333 16603
        if (! $constraintName) {
334 16557
            $constraintName = $this->_generateIdentifierName(
335 16557
                array_merge((array) $this->getName(), $localColumnNames),
336 16557
                'fk',
337 16557
                $this->_getMaxIdentifierLength()
338
            );
339
        }
340
341 16603
        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 10313
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = []) : self
357
    {
358 10313
        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 16604
    public function addNamedForeignKeyConstraint(string $name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = []) : self
374
    {
375 16604
        if ($foreignTable instanceof Table) {
376 16195
            foreach ($foreignColumnNames as $columnName) {
377 16195
                if (! $foreignTable->hasColumn($columnName)) {
378 1054
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
379
                }
380
            }
381
        }
382
383 16603
        foreach ($localColumnNames as $columnName) {
384 16603
            if (! $this->hasColumn($columnName)) {
385 1122
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
386
            }
387
        }
388
389 16602
        $constraint = new ForeignKeyConstraint(
390 15972
            $localColumnNames,
391 89
            $foreignTable,
392 89
            $foreignColumnNames,
393 89
            $name,
394 719
            $options
395
        );
396
397 16602
        return $this->_addForeignKeyConstraint($constraint);
398
    }
399
400
    /**
401
     * @param mixed $value
402
     */
403 14718
    public function addOption(string $name, $value) : self
404
    {
405 14718
        $this->_options[$name] = $value;
406
407 14718
        return $this;
408
    }
409
410
    /**
411
     * Returns whether this table has a foreign key constraint with the given name.
412
     */
413 14376
    public function hasForeignKey(string $constraintName) : bool
414
    {
415 14376
        $constraintName = $this->normalizeIdentifier($constraintName);
416
417 14376
        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 321
    public function getForeignKey(string $constraintName) : ForeignKeyConstraint
428
    {
429 321
        $constraintName = $this->normalizeIdentifier($constraintName);
430
431 321
        if (! $this->hasForeignKey($constraintName)) {
432
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
433
        }
434
435 321
        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 322
    public function removeForeignKey(string $constraintName) : void
446
    {
447 322
        $constraintName = $this->normalizeIdentifier($constraintName);
448
449 322
        if (! $this->hasForeignKey($constraintName)) {
450
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
451
        }
452
453 322
        unset($this->_fkConstraints[$constraintName]);
454 322
    }
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 17388
    public function getColumns() : array
508
    {
509 17388
        $columns = $this->_columns;
510 17388
        $pkCols  = [];
511 17388
        $fkCols  = [];
512
513 17388
        $primaryKey = $this->getPrimaryKey();
514
515 17388
        if ($primaryKey !== null) {
516 17150
            $pkCols = $primaryKey->getColumns();
517
        }
518
519 17388
        foreach ($this->getForeignKeys() as $fk) {
520
            /** @var ForeignKeyConstraint $fk */
521 16636
            $fkCols = array_merge($fkCols, $fk->getColumns());
522
        }
523
524 17388
        $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
525
526
        uksort($columns, static function ($a, $b) use ($colNames) : bool {
527 17227
            return array_search($a, $colNames) >= array_search($b, $colNames);
528 17388
        });
529
530 17388
        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 17415
    public function hasColumn(string $columnName) : bool
539
    {
540 17415
        $columnName = $this->normalizeIdentifier($columnName);
541
542 17415
        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 17311
    public function getColumn(string $columnName) : Column
553
    {
554 17311
        $columnName = $this->normalizeIdentifier($columnName);
555
556 17311
        if (! $this->hasColumn($columnName)) {
557 1417
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
558
        }
559
560 17310
        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 17393
    public function getPrimaryKey() : ?Index
569
    {
570 17393
        return $this->hasPrimaryKey()
571 17155
            ? $this->getIndex($this->_primaryKeyName)
572 17393
            : null;
573
    }
574
575
    /**
576
     * Returns the primary key columns.
577
     *
578
     * @return string[]
579
     *
580
     * @throws DBALException
581
     */
582 14556
    public function getPrimaryKeyColumns() : array
583
    {
584 14556
        $primaryKey = $this->getPrimaryKey();
585
586 14556
        if ($primaryKey === null) {
587
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
588
        }
589
590 14556
        return $primaryKey->getColumns();
591
    }
592
593
    /**
594
     * Returns whether this table has a primary key.
595
     */
596 17396
    public function hasPrimaryKey() : bool
597
    {
598 17396
        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 17207
    public function hasIndex(string $indexName) : bool
607
    {
608 17207
        $indexName = $this->normalizeIdentifier($indexName);
609
610 17207
        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 17185
    public function getIndex(string $indexName) : Index
621
    {
622 17185
        $indexName = $this->normalizeIdentifier($indexName);
623
624 17185
        if (! $this->hasIndex($indexName)) {
625 1297
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
626
        }
627
628 17184
        return $this->_indexes[$indexName];
629
    }
630
631
    /**
632
     * @return Index[]
633
     */
634 17375
    public function getIndexes() : array
635
    {
636 17375
        return $this->_indexes;
637
    }
638
639
    /**
640
     * Returns the unique constraints.
641
     *
642
     * @return UniqueConstraint[]
643
     */
644 17264
    public function getUniqueConstraints() : array
645
    {
646 17264
        return $this->_uniqueConstraints;
647
    }
648
649
    /**
650
     * Returns the foreign key constraints.
651
     *
652
     * @return ForeignKeyConstraint[]
653
     */
654 17406
    public function getForeignKeys() : array
655
    {
656 17406
        return $this->_fkConstraints;
657
    }
658
659 14656
    public function hasOption(string $name) : bool
660
    {
661 14656
        return isset($this->_options[$name]);
662
    }
663
664
    /**
665
     * @return mixed
666
     */
667 14221
    public function getOption(string $name)
668
    {
669 14221
        return $this->_options[$name];
670
    }
671
672
    /**
673
     * @return mixed[]
674
     */
675 17278
    public function getOptions() : array
676
    {
677 17278
        return $this->_options;
678
    }
679
680 16429
    public function visit(Visitor $visitor) : void
681
    {
682 16429
        $visitor->acceptTable($this);
683
684 16429
        foreach ($this->getColumns() as $column) {
685 16426
            $visitor->acceptColumn($this, $column);
686
        }
687
688 16429
        foreach ($this->getIndexes() as $index) {
689 14881
            $visitor->acceptIndex($this, $index);
690
        }
691
692 16429
        foreach ($this->getForeignKeys() as $constraint) {
693 148
            $visitor->acceptForeignKey($this, $constraint);
694
        }
695 16429
    }
696
697
    /**
698
     * Clone of a Table triggers a deep clone of all affected assets.
699
     */
700 15819
    public function __clone()
701
    {
702 15819
        foreach ($this->_columns as $k => $column) {
703 15818
            $this->_columns[$k] = clone $column;
704
        }
705
706 15819
        foreach ($this->_indexes as $k => $index) {
707 15807
            $this->_indexes[$k] = clone $index;
708
        }
709
710 15819
        foreach ($this->_fkConstraints as $k => $fk) {
711 14854
            $this->_fkConstraints[$k] = clone $fk;
712 14854
            $this->_fkConstraints[$k]->setLocalTable($this);
713
        }
714 15819
    }
715
716 16768
    protected function _getMaxIdentifierLength() : int
717
    {
718 16768
        return $this->_schemaConfig instanceof SchemaConfig
719 16507
            ? $this->_schemaConfig->getMaxIdentifierLength()
720 16768
            : 63;
721
    }
722
723
    /**
724
     * @throws SchemaException
725
     */
726 17540
    protected function _addColumn(Column $column) : void
727
    {
728 17540
        $columnName = $column->getName();
729 17540
        $columnName = $this->normalizeIdentifier($columnName);
730
731 17540
        if (isset($this->_columns[$columnName])) {
732 1393
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
733
        }
734
735 17540
        $this->_columns[$columnName] = $column;
736 17540
    }
737
738
    /**
739
     * Adds an index to the table.
740
     *
741
     * @throws SchemaException
742
     */
743 17355
    protected function _addIndex(Index $indexCandidate) : self
744
    {
745 17355
        $indexName               = $indexCandidate->getName();
746 17355
        $indexName               = $this->normalizeIdentifier($indexName);
747 17355
        $replacedImplicitIndexes = [];
748
749 17355
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
750 13308
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
751 13304
                continue;
752
            }
753
754 724
            $replacedImplicitIndexes[] = $name;
755
        }
756
757 17355
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
758 17355
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
759
        ) {
760 1274
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
761
        }
762
763 17355
        foreach ($replacedImplicitIndexes as $name) {
764 724
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
765
        }
766
767 17355
        if ($indexCandidate->isPrimary()) {
768 17233
            $this->_primaryKeyName = $indexName;
769
        }
770
771 17355
        $this->_indexes[$indexName] = $indexCandidate;
772
773 17355
        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 16686
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) : self
811
    {
812 16686
        $constraint->setLocalTable($this);
813
814 16686
        $name = $constraint->getName() ?? $this->_generateIdentifierName(
815 1796
            array_merge([$this->getName()], $constraint->getLocalColumns()),
816 1796
            'fk',
817 16686
            $this->_getMaxIdentifierLength()
818
        );
819
820 16686
        $name = $this->normalizeIdentifier($name);
821
822 16686
        $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 16686
        $indexName = $this->_generateIdentifierName(
829 16686
            array_merge([$this->getName()], $constraint->getColumns()),
830 16686
            'idx',
831 16686
            $this->_getMaxIdentifierLength()
832
        );
833
834 16686
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
835
836 16686
        foreach ($this->_indexes as $existingIndex) {
837 16660
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
838 15308
                return $this;
839
            }
840
        }
841
842 16662
        $this->_addIndex($indexCandidate);
843 16662
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
844
845 16662
        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 17544
    private function normalizeIdentifier(?string $identifier) : string
858
    {
859 17544
        if ($identifier === null) {
860 433
            return '';
861
        }
862
863 17544
        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 17350
    private function _createIndex(array $columns, string $indexName, bool $isUnique, bool $isPrimary, array $flags = [], array $options = []) : Index
902
    {
903 17350
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
904 1105
            throw SchemaException::indexNameInvalid($indexName);
905
        }
906
907 17349
        foreach ($columns as $index => $value) {
908 17348
            if (is_string($index)) {
909
                $columnName = $index;
910
            } else {
911 17348
                $columnName = $value;
912
            }
913
914 17348
            if (! $this->hasColumn($columnName)) {
915 1476
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
916
            }
917
        }
918
919 17348
        return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
920
    }
921
}
922