Failed Conditions
Pull Request — develop (#3348)
by Sergei
72:11 queued 07: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
    public function __construct(
59
        string $tableName,
60
        array $columns = [],
61
        array $indexes = [],
62 16040
        array $uniqueConstraints = [],
63
        array $fkConstraints = [],
64
        array $options = []
65
    ) {
66
        if (strlen($tableName) === 0) {
67
            throw DBALException::invalidTableName($tableName);
68
        }
69
70 16040
        $this->_setName($tableName);
71 23
72
        foreach ($columns as $column) {
73
            $this->_addColumn($column);
74 16017
        }
75
76 16017
        foreach ($indexes as $idx) {
77 1747
            $this->_addIndex($idx);
78
        }
79
80 15994
        foreach ($uniqueConstraints as $uniqueConstraint) {
81 670
            $this->_addUniqueConstraint($uniqueConstraint);
82
        }
83
84 15948
        foreach ($fkConstraints as $fkConstraint) {
85
            $this->_addForeignKeyConstraint($fkConstraint);
86
        }
87
88 15948
        $this->_options = $options;
89 264
    }
90
91
    public function getName() : string
92 15948
    {
93 15948
        $name = parent::getName();
94
        assert(is_string($name));
95
96
        return $name;
97
    }
98 1534
99
    public function setSchemaConfig(SchemaConfig $schemaConfig) : void
100 1534
    {
101 1534
        $this->_schemaConfig = $schemaConfig;
102
    }
103
104
    /**
105
     * Sets the Primary Key.
106
     *
107
     * @param string[]     $columnNames
108
     * @param string|false $indexName
109
     */
110
    public function setPrimaryKey(array $columnNames, $indexName = false) : self
111 6625
    {
112
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
113 6625
114
        foreach ($columnNames as $columnName) {
115 6625
            $column = $this->getColumn($columnName);
116 6625
            $column->setNotnull(true);
117 6625
        }
118
119
        return $this;
120 6625
    }
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
            $indexName = $this->_generateIdentifierName(
149
                array_merge([$this->getName()], $columnNames),
150
                'idx',
151
                $this->_getMaxIdentifierLength()
152 2044
            );
153
        }
154 2044
155 457
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
156 457
    }
157 457
158 457
    /**
159
     * Drops the primary key from this table.
160
     */
161
    public function dropPrimaryKey() : void
162 2044
    {
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
167
    /**
168
     * Drops an index from this table.
169
     *
170 426
     * @param string $indexName The index name.
171
     *
172 426
     * @throws SchemaException If the index does not exist.
173 426
     */
174 426
    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
182
        unset($this->_indexes[$indexName]);
183
    }
184
185 737
    /**
186
     * @param string[] $columnNames
187 737
     * @param mixed[]  $options
188
     */
189 737
    public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []) : self
190
    {
191
        if ($indexName === null) {
192
            $indexName = $this->_generateIdentifierName(
193 737
                array_merge([$this->getName()], $columnNames),
194 737
                'uniq',
195
                $this->_getMaxIdentifierLength()
196
            );
197
        }
198
199
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
200
    }
201
202
    /**
203 614
     * Renames an index.
204
     *
205 614
     * @param string      $oldIndexName The name of the index to rename from.
206 423
     * @param string|null $newIndexName The name of the index to rename to.
207 423
     *                                  If null is given, the index name will be auto-generated.
208 423
     *
209 423
     * @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 614
     */
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
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
225
        }
226
227
        if ($this->hasIndex($normalizedNewIndexName)) {
228 321
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
229
        }
230 321
231 321
        $oldIndex = $this->_indexes[$oldIndexName];
232
233 321
        if ($oldIndex->isPrimary()) {
234 207
            $this->dropPrimaryKey();
235
236
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
237 321
        }
238 23
239
        unset($this->_indexes[$oldIndexName]);
240
241 298
        if ($oldIndex->isUnique()) {
242 23
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
243
        }
244
245 275
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
246
    }
247 275
248 23
    /**
249
     * Checks if an index begins in the order of the given columns.
250 23
     *
251
     * @param string[] $columnNames
252
     */
253 275
    public function columnsAreIndexed(array $columnNames) : bool
254
    {
255 275
        foreach ($this->getIndexes() as $index) {
256 46
            /** @var $index Index */
257
            if ($index->spansColumns($columnNames)) {
258
                return true;
259 252
            }
260
        }
261
262
        return false;
263
    }
264
265
    /**
266
     * @param mixed[] $options
267
     */
268
    public function addColumn(string $columnName, string $typeName, array $options = []) : Column
269 45
    {
270
        $column = new Column($columnName, Type::getType($typeName), $options);
271 45
272
        $this->_addColumn($column);
273 45
274 45
        return $column;
275
    }
276
277
    /**
278
     * Renames a Column.
279
     *
280
     * @deprecated
281
     *
282
     * @throws DBALException
283
     */
284
    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
        throw new DBALException(
287
            'Table#renameColumn() was removed, because it drops and recreates the column instead. ' .
288 13231
            '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 13231
        );
291
    }
292 13231
293
    /**
294 13231
     * 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
     * Adds a foreign key constraint.
321
     *
322
     * Name is inferred from the local columns.
323
     *
324 277
     * @param Table|string $foreignTable       Table schema instance or table name
325
     * @param string[]     $localColumnNames
326 277
     * @param string[]     $foreignColumnNames
327
     * @param mixed[]      $options
328 277
     * @param string|null  $constraintName
329
     */
330 277
    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
                $this->_getMaxIdentifierLength()
337
            );
338
        }
339
340 207
        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 207
343
    /**
344 207
     * Adds a foreign key constraint.
345
     *
346 207
     * 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
    }
359
360
    /**
361
     * Adds a foreign key constraint with a given name.
362 2054
     *
363
     * @deprecated Use {@link addForeignKeyConstraint}
364 2054
     *
365 1019
     * @param Table|string $foreignTable       Table schema instance or table name
366 1019
     * @param string[]     $localColumnNames
367 1019
     * @param string[]     $foreignColumnNames
368 1019
     * @param mixed[]      $options
369
     *
370
     * @throws SchemaException
371
     */
372 2054
    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
            }
386
        }
387
388
        $constraint = new ForeignKeyConstraint(
389 98
            $localColumnNames,
390
            $foreignTable,
391 98
            $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
406
        return $this;
407
    }
408
409 2077
    /**
410
     * Returns whether this table has a foreign key constraint with the given name.
411 2077
     */
412 1054
    public function hasForeignKey(string $constraintName) : bool
413 1054
    {
414 1054
        $constraintName = $this->normalizeIdentifier($constraintName);
415
416
        return isset($this->_fkConstraints[$constraintName]);
417
    }
418
419 2054
    /**
420 2054
     * Returns the foreign key constraint with the given name.
421 2054
     *
422
     * @param string $constraintName The constraint name.
423
     *
424
     * @throws SchemaException If the foreign key does not exist.
425 2031
     */
426 2031
    public function getForeignKey(string $constraintName) : ForeignKeyConstraint
427 2031
    {
428 2031
        $constraintName = $this->normalizeIdentifier($constraintName);
429 2031
430 2031
        if (! $this->hasForeignKey($constraintName)) {
431
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
432
        }
433 2031
434
        return $this->_fkConstraints[$constraintName];
435
    }
436
437
    /**
438
     * Removes the foreign key constraint with the given name.
439
     *
440
     * @param string $constraintName The constraint name.
441
     *
442 1084
     * @throws SchemaException
443
     */
444 1084
    public function removeForeignKey(string $constraintName) : void
445
    {
446 1084
        $constraintName = $this->normalizeIdentifier($constraintName);
447
448
        if (! $this->hasForeignKey($constraintName)) {
449
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
450
        }
451
452
        unset($this->_fkConstraints[$constraintName]);
453
    }
454
455
    /**
456 253
     * Returns whether this table has a unique constraint with the given name.
457
     */
458 253
    public function hasUniqueConstraint(string $constraintName) : bool
459
    {
460 253
        $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
     * @param string $constraintName The constraint name.
469
     *
470
     * @throws SchemaException If the foreign key does not exist.
471
     */
472 185
    public function getUniqueConstraint(string $constraintName) : UniqueConstraint
473
    {
474 185
        $constraintName = $this->normalizeIdentifier($constraintName);
475
476 185
        if (! $this->hasUniqueConstraint($constraintName)) {
477
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
478
        }
479
480 185
        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
     * @throws SchemaException
489
     */
490
    public function removeUniqueConstraint(string $constraintName) : void
491
    {
492 230
        $constraintName = $this->normalizeIdentifier($constraintName);
493
494 230
        if (! $this->hasUniqueConstraint($constraintName)) {
495
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
496 230
        }
497
498
        unset($this->_uniqueConstraints[$constraintName]);
499
    }
500 230
501 230
    /**
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
559
        return $this->_columns[$columnName];
560
    }
561
562 10360
    /**
563
     * Returns the primary key.
564 10360
     *
565 10360
     * @return Index|null The primary key, or null if this Table has no primary key.
566 10360
     */
567
    public function getPrimaryKey() : ?Index
568 10360
    {
569 4768
        return $this->hasPrimaryKey()
570
            ? $this->getIndex($this->_primaryKeyName)
571
            : null;
572 10360
    }
573
574 1003
    /**
575
     * Returns the primary key columns.
576
     *
577 10360
     * @return string[]
578
     *
579
     * @throws DBALException
580 6482
     */
581 10360
    public function getPrimaryKeyColumns() : array
582
    {
583 10360
        $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
592
    /**
593 10972
     * Returns whether this table has a primary key.
594
     */
595 10972
    public function hasPrimaryKey() : bool
596
    {
597 10972
        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
        $indexName = $this->normalizeIdentifier($indexName);
608
609 8563
        return isset($this->_indexes[$indexName]);
610
    }
611 8563
612
    /**
613 8563
     * Returns the Index with the given name.
614 23
     *
615
     * @param string $indexName The index name.
616
     *
617 8540
     * @throws SchemaException If the index does not exist.
618
     */
619
    public function getIndex(string $indexName) : Index
620
    {
621
        $indexName = $this->normalizeIdentifier($indexName);
622
623
        if (! $this->hasIndex($indexName)) {
624
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
625 4883
        }
626
627 4883
        return $this->_indexes[$indexName];
628 4883
    }
629 4883
630
    /**
631
     * @return Index[]
632
     */
633
    public function getIndexes() : array
634
    {
635
        return $this->_indexes;
636
    }
637
638
    /**
639 311
     * Returns the unique constraints.
640
     *
641 311
     * @return UniqueConstraint[]
642
     */
643
    public function getUniqueConstraints() : array
644 311
    {
645
        return $this->_uniqueConstraints;
646
    }
647
648
    /**
649
     * Returns the foreign key constraints.
650
     *
651
     * @return ForeignKeyConstraint[]
652 10544
     */
653
    public function getForeignKeys() : array
654 10544
    {
655
        return $this->_fkConstraints;
656
    }
657
658
    public function hasOption(string $name) : bool
659
    {
660
        return isset($this->_options[$name]);
661
    }
662
663
    /**
664 6109
     * @return mixed
665
     */
666 6109
    public function getOption(string $name)
667
    {
668 6109
        return $this->_options[$name];
669
    }
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 5603
    {
681
        $visitor->acceptTable($this);
682 5603
683
        foreach ($this->getColumns() as $column) {
684 5603
            $visitor->acceptColumn($this, $column);
685 23
        }
686
687
        foreach ($this->getIndexes() as $index) {
688 5580
            $visitor->acceptIndex($this, $index);
689
        }
690
691
        foreach ($this->getForeignKeys() as $constraint) {
692
            $visitor->acceptForeignKey($this, $constraint);
693
        }
694 10061
    }
695
696 10061
    /**
697
     * 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 7476
            $this->_columns[$k] = clone $column;
705
        }
706 7476
707
        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 10774
        }
715
    }
716 10774
717
    protected function _getMaxIdentifierLength() : int
718
    {
719
        return $this->_schemaConfig instanceof SchemaConfig
720
            ? $this->_schemaConfig->getMaxIdentifierLength()
721
            : 63;
722
    }
723
724 1915
    /**
725
     * @throws SchemaException
726 1915
     */
727
    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 186
        }
735
736 186
        $this->_columns[$columnName] = $column;
737
    }
738
739
    /**
740
     * Adds an index to the table.
741
     *
742 7798
     * @throws SchemaException
743
     */
744 7798
    protected function _addIndex(Index $indexCandidate) : self
745
    {
746
        $indexName               = $indexCandidate->getName();
747
        $indexName               = $this->normalizeIdentifier($indexName);
748
        $replacedImplicitIndexes = [];
749
750 405
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
751
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
752 405
                continue;
753
            }
754 405
755 336
            $replacedImplicitIndexes[] = $name;
756
        }
757
758 405
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
759 247
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
760
        ) {
761
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
762 405
        }
763 92
764
        foreach ($replacedImplicitIndexes as $name) {
765 405
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
766
        }
767
768
        if ($indexCandidate->isPrimary()) {
769
            $this->_primaryKeyName = $indexName;
770
        }
771
772 1083
        $this->_indexes[$indexName] = $indexCandidate;
773
774 1083
        return $this;
775 1060
    }
776
777
    protected function _addUniqueConstraint(UniqueConstraint $constraint) : self
778 1083
    {
779 785
        $name = $constraint->getName() ?? $this->_generateIdentifierName(
780
            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 1083
            $this->_getMaxIdentifierLength()
783 183
        );
784 183
785
        $name = $this->normalizeIdentifier($name);
786 1083
787
        $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 3001
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
792
        $indexName = $this->_generateIdentifierName(
793 3001
            array_merge([$this->getName()], $constraint->getColumns()),
794 237
            'idx',
795 3001
            $this->_getMaxIdentifierLength()
796
        );
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 13947
            }
804
        }
805 13947
806 13947
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
807
808 13947
        return $this;
809 23
    }
810
811
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint) : self
812 13947
    {
813 13947
        $constraint->setLocalTable($this);
814
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 9518
823
        $this->_fkConstraints[$name] = $constraint;
824 9518
825 9518
        // add an explicit index on the foreign key columns.
826 9518
        // If there is already an index that fulfills this requirements drop the request. In the case of __construct
827
        // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates.
828 9518
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
829 624
        $indexName = $this->_generateIdentifierName(
830 532
            array_merge([$this->getName()], $constraint->getColumns()),
831
            'idx',
832
            $this->_getMaxIdentifierLength()
833 92
        );
834
835
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
836 9518
837 9518
        foreach ($this->_indexes as $existingIndex) {
838
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
839 46
                return $this;
840
            }
841
        }
842 9518
843 92
        $this->_addIndex($indexCandidate);
844
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
845
846 9518
        return $this;
847 6695
    }
848
849
    /**
850 9518
     * Normalizes a given identifier.
851
     *
852 9518
     * Trims quotes and lowercases the given identifier.
853
     *
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 2141
     * @param string[] $flags
898
     * @param mixed[]  $options
899 2141
     *
900
     * @throws SchemaException
901 2141
     */
902 2117
    private function _createIndex(array $columns, string $indexName, bool $isUnique, bool $isPrimary, array $flags = [], array $options = []) : Index
903 24
    {
904 24
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
905 24
            throw SchemaException::indexNameInvalid($indexName);
906 2141
        }
907
908
        foreach ($columns as $index => $value) {
909 2141
            if (is_string($index)) {
910
                $columnName = $index;
911 2141
            } else {
912
                $columnName = $value;
913
            }
914
915
            if (! $this->hasColumn($columnName)) {
916
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
917 2141
            }
918 2141
        }
919 2141
920 2141
        return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
921
    }
922
}
923