Failed Conditions
Pull Request — develop (#3348)
by Sergei
125:02 queued 59:58
created

Table::getName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
eloc 3
dl 0
loc 6
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\Visitor\Visitor;
7
use Doctrine\DBAL\Types\Type;
8
use function array_keys;
9
use function array_merge;
10
use function array_search;
11
use function array_unique;
12
use function assert;
13
use function in_array;
14
use function is_string;
15
use function preg_match;
16
use function strlen;
17
use function strtolower;
18
use function uksort;
19
20
/**
21
 * Object Representation of a table.
22
 */
23
class Table extends AbstractAsset
24
{
25
    /** @var Column[] */
26
    protected $_columns = [];
27
28
    /** @var Index[] */
29
    private $implicitIndexes = [];
30
31
    /** @var Index[] */
32
    protected $_indexes = [];
33
34
    /** @var string */
35
    protected $_primaryKeyName = false;
36
37
    /** @var UniqueConstraint[] */
38
    protected $_uniqueConstraints = [];
39
40
    /** @var ForeignKeyConstraint[] */
41
    protected $_fkConstraints = [];
42
43
    /** @var mixed[] */
44
    protected $_options = [];
45
46
    /** @var SchemaConfig|null */
47
    protected $_schemaConfig = null;
48
49
    /**
50
     * @param Column[]               $columns
51
     * @param Index[]                $indexes
52
     * @param UniqueConstraint[]     $uniqueConstraints
53
     * @param ForeignKeyConstraint[] $fkConstraints
54
     * @param mixed[]                $options
55
     *
56
     * @throws DBALException
57
     */
58 17654
    public function __construct(
59
        string $tableName,
60
        array $columns = [],
61
        array $indexes = [],
62
        array $uniqueConstraints = [],
63
        array $fkConstraints = [],
64
        array $options = []
65
    ) {
66 17654
        if (strlen($tableName) === 0) {
67 1561
            throw DBALException::invalidTableName($tableName);
68
        }
69
70 17653
        $this->_setName($tableName);
71
72 17653
        foreach ($columns as $column) {
73 16192
            $this->_addColumn($column);
74
        }
75
76 17652
        foreach ($indexes as $idx) {
77 16119
            $this->_addIndex($idx);
78
        }
79
80 17650
        foreach ($uniqueConstraints as $uniqueConstraint) {
81
            $this->_addUniqueConstraint($uniqueConstraint);
82
        }
83
84 17650
        foreach ($fkConstraints as $fkConstraint) {
85 15348
            $this->_addForeignKeyConstraint($fkConstraint);
86
        }
87
88 17650
        $this->_options = $options;
89 17650
    }
90
91
    public function getName() : string
92
    {
93
        $name = parent::getName();
94 16674
        assert(is_string($name));
95
96 16674
        return $name;
97 16674
    }
98
99
    public function setSchemaConfig(SchemaConfig $schemaConfig) : void
100
    {
101
        $this->_schemaConfig = $schemaConfig;
102
    }
103
104
    /**
105
     * Sets the Primary Key.
106
     *
107 17254
     * @param string[]     $columnNames
108
     * @param string|false $indexName
109 17254
     */
110
    public function setPrimaryKey(array $columnNames, $indexName = false) : self
111 17254
    {
112 17254
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
113 17254
114
        foreach ($columnNames as $columnName) {
115
            $column = $this->getColumn($columnName);
116 17254
            $column->setNotnull(true);
117
        }
118
119
        return $this;
120
    }
121
122
    /**
123
     * @param mixed[]  $columnNames
124
     * @param string[] $flags
125
     * @param mixed[]  $options
126
     */
127
    public function addUniqueConstraint(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
128
    {
129
        if ($indexName === null) {
130
            $indexName = $this->_generateIdentifierName(
131
                array_merge([$this->getName()], $columnNames),
132
                'uniq',
133
                $this->_getMaxIdentifierLength()
134
            );
135
        }
136
137
        return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options));
138
    }
139
140
    /**
141
     * @param string[] $columnNames
142
     * @param string[] $flags
143
     * @param mixed[]  $options
144
     */
145
    public function addIndex(array $columnNames, ?string $indexName = null, array $flags = [], array $options = []) : self
146
    {
147
        if ($indexName === null) {
148 15724
            $indexName = $this->_generateIdentifierName(
149
                array_merge([$this->getName()], $columnNames),
150 15724
                'idx',
151 15322
                $this->_getMaxIdentifierLength()
152 15322
            );
153 15322
        }
154 15322
155
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
156
    }
157
158 15724
    /**
159
     * Drops the primary key from this table.
160
     */
161
    public function dropPrimaryKey() : void
162
    {
163
        $this->dropIndex($this->_primaryKeyName);
164
        $this->_primaryKeyName = false;
0 ignored issues
show
Documentation Bug introduced by
The property $_primaryKeyName was declared of type string, but false is of type false. Maybe add a type cast?

This check looks for assignments to scalar types that may be of the wrong type.

To ensure the code behaves as expected, it may be a good idea to add an explicit type cast.

$answer = 42;

$correct = false;

$correct = (bool) $answer;
Loading history...
165
    }
166 14581
167
    /**
168 14581
     * Drops an index from this table.
169 14581
     *
170 14581
     * @param string $indexName The index name.
171
     *
172
     * @throws SchemaException If the index does not exist.
173
     */
174
    public function dropIndex(string $indexName) : void
175
    {
176
        $indexName = $this->normalizeIdentifier($indexName);
177
178
        if (! $this->hasIndex($indexName)) {
179
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
180
        }
181 14604
182
        unset($this->_indexes[$indexName]);
183 14604
    }
184
185 14604
    /**
186
     * @param string[] $columnNames
187
     * @param mixed[]  $options
188
     */
189 14604
    public function addUniqueIndex(array $columnNames, ?string $indexName = null, array $options = []) : self
190 14604
    {
191
        if ($indexName === null) {
192
            $indexName = $this->_generateIdentifierName(
193
                array_merge([$this->getName()], $columnNames),
194
                'uniq',
195
                $this->_getMaxIdentifierLength()
196
            );
197
        }
198
199 16559
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
200
    }
201 16559
202 16227
    /**
203 16227
     * Renames an index.
204 16227
     *
205 16227
     * @param string      $oldIndexName The name of the index to rename from.
206
     * @param string|null $newIndexName The name of the index to rename to.
207
     *                                  If null is given, the index name will be auto-generated.
208
     *
209 16559
     * @return self This table instance.
210
     *
211
     * @throws SchemaException If no index exists for the given current name
212
     *                         or if an index with the given new name already exists on this table.
213
     */
214
    public function renameIndex(string $oldIndexName, ?string $newIndexName = null) : self
215
    {
216
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
217
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
218
219
        if ($oldIndexName === $normalizedNewIndexName) {
220
            return $this;
221
        }
222
223
        if (! $this->hasIndex($oldIndexName)) {
224 14401
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
225
        }
226 14401
227 14401
        if ($this->hasIndex($normalizedNewIndexName)) {
228
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
229 14401
        }
230 441
231
        $oldIndex = $this->_indexes[$oldIndexName];
232
233 14401
        if ($oldIndex->isPrimary()) {
234 361
            $this->dropPrimaryKey();
235
236
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
237 14400
        }
238 337
239
        unset($this->_indexes[$oldIndexName]);
240
241 14399
        if ($oldIndex->isUnique()) {
242
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
243 14399
        }
244 433
245
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
246 433
    }
247
248
    /**
249 14399
     * Checks if an index begins in the order of the given columns.
250
     *
251 14399
     * @param string[] $columnNames
252 434
     */
253
    public function columnsAreIndexed(array $columnNames) : bool
254
    {
255 14398
        foreach ($this->getIndexes() as $index) {
256
            /** @var $index Index */
257
            if ($index->spansColumns($columnNames)) {
258
                return true;
259
            }
260
        }
261
262
        return false;
263
    }
264
265 14581
    /**
266
     * @param mixed[] $options
267 14581
     */
268
    public function addColumn(string $columnName, string $typeName, array $options = []) : Column
269 14581
    {
270 14581
        $column = new Column($columnName, Type::getType($typeName), $options);
271
272
        $this->_addColumn($column);
273
274
        return $column;
275
    }
276
277
    /**
278
     * Renames a Column.
279
     *
280
     * @deprecated
281
     *
282
     * @throws DBALException
283
     */
284 17534
    public function renameColumn(string $oldColumnName, string $newColumnName) : void
0 ignored issues
show
Unused Code introduced by
The parameter $oldColumnName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

284
    public function renameColumn(/** @scrutinizer ignore-unused */ string $oldColumnName, string $newColumnName) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $newColumnName is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

284
    public function renameColumn(string $oldColumnName, /** @scrutinizer ignore-unused */ string $newColumnName) : void

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

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

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

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