Failed Conditions
Pull Request — master (#3260)
by Michael
61:30
created

Table::_createUniqueConstraint()   A

Complexity

Conditions 5
Paths 6

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 30

Importance

Changes 0
Metric Value
eloc 10
dl 0
loc 19
ccs 0
cts 0
cp 0
rs 9.6111
c 0
b 0
f 0
cc 5
nc 6
nop 4
crap 30
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 in_array;
15
use function is_string;
16
use function preg_match;
17
use function strlen;
18
use function strtolower;
19
use function uksort;
20
21
/**
22
 * Object Representation of a table.
23
 */
24
class Table extends AbstractAsset
25
{
26
    /** @var Column[] */
27
    protected $_columns = [];
28
29
    /** @var Index[] */
30
    private $implicitIndexes = [];
31
32
    /** @var Index[] */
33
    protected $_indexes = [];
34
35
    /** @var string */
36
    protected $_primaryKeyName = false;
37
38
    /** @var UniqueConstraint[] */
39
    protected $_uniqueConstraints = [];
40
41
    /** @var ForeignKeyConstraint[] */
42
    protected $_fkConstraints = [];
43
44
    /** @var mixed[] */
45
    protected $_options = [];
46
47
    /** @var SchemaConfig|null */
48
    protected $_schemaConfig = null;
49
50
    /**
51
     * @param string                 $tableName
52 26322
     * @param Column[]               $columns
53
     * @param Index[]                $indexes
54 26322
     * @param UniqueConstraint[]     $uniqueConstraints
55 1757
     * @param ForeignKeyConstraint[] $fkConstraints
56
     * @param mixed[]                $options
57
     *
58 26320
     * @throws DBALException
59
     */
60 26320
    public function __construct(
61 23771
        $tableName,
62
        array $columns = [],
63
        array $indexes = [],
64 26318
        array $uniqueConstraints = [],
65 23642
        array $fkConstraints = [],
66
        array $options = []
67
    ) {
68 26314
        if (strlen($tableName) === 0) {
69 22724
            throw DBALException::invalidTableName($tableName);
70
        }
71
72 26314
        $this->_setName($tableName);
73 26314
74
        foreach ($columns as $column) {
75
            $this->_addColumn($column);
76
        }
77
78 24306
        foreach ($indexes as $idx) {
79
            $this->_addIndex($idx);
80 24306
        }
81 24306
82
        foreach ($uniqueConstraints as $uniqueConstraint) {
83
            $this->_addUniqueConstraint($uniqueConstraint);
84
        }
85
86 24582
        foreach ($fkConstraints as $fkConstraint) {
87
            $this->_addForeignKeyConstraint($fkConstraint);
88 24582
        }
89 24096
90
        $this->_options = $options;
91
    }
92 24566
93
    /**
94
     * @return void
95
     */
96
    public function setSchemaConfig(SchemaConfig $schemaConfig)
97
    {
98
        $this->_schemaConfig = $schemaConfig;
99
    }
100
101
    /**
102
     * Sets the Primary Key.
103 25284
     *
104
     * @param string[]     $columnNames
105 25284
     * @param string|false $indexName
106
     *
107 25284
     * @return self
108 25284
     */
109 25284
    public function setPrimaryKey(array $columnNames, $indexName = false)
110
    {
111
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
112 25284
113
        foreach ($columnNames as $columnName) {
114
            $column = $this->getColumn($columnName);
115
            $column->setNotnull(true);
116
        }
117
118
        return $this;
119
    }
120
121
    /**
122
     * @param mixed[]     $columnNames
123 23281
     * @param string|null $indexName
124
     * @param string[]    $flags
125 23281
     * @param mixed[]     $options
126 22848
     *
127 22848
     * @return self
128 22848
     */
129 22848
    public function addUniqueConstraint(array $columnNames, $indexName = null, array $flags = [], array $options = [])
130
    {
131
        if ($indexName === null) {
132
            $indexName = $this->_generateIdentifierName(
133 23281
                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 22071
142
    /**
143 22071
     * @param string[]    $columnNames
144 22071
     * @param string|null $indexName
145 22071
     * @param string[]    $flags
146
     * @param mixed[]     $options
147
     *
148
     * @return self
149
     */
150
    public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
151
    {
152
        if ($indexName === null) {
153
            $indexName = $this->_generateIdentifierName(
154
                array_merge([$this->getName()], $columnNames),
155
                'idx',
156 22107
                $this->_getMaxIdentifierLength()
157
            );
158 22107
        }
159 22107
160
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
161
    }
162 22107
163 22107
    /**
164
     * Drops the primary key from this table.
165
     *
166
     * @return void
167
     */
168
    public function dropPrimaryKey()
169
    {
170
        $this->dropIndex($this->_primaryKeyName);
171
        $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...
172 24154
    }
173
174 24154
    /**
175 23809
     * Drops an index from this table.
176 23809
     *
177 23809
     * @param string $indexName The index name.
178 23809
     *
179
     * @return void
180
     *
181
     * @throws SchemaException If the index does not exist.
182 24154
     */
183
    public function dropIndex($indexName)
184
    {
185
        $indexName = $this->normalizeIdentifier($indexName);
186
187
        if (! $this->hasIndex($indexName)) {
188
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
189
        }
190
191
        unset($this->_indexes[$indexName]);
192
    }
193
194
    /**
195
     * @param string[]    $columnNames
196
     * @param string|null $indexName
197 22325
     * @param mixed[]     $options
198
     *
199 22325
     * @return self
200 22325
     */
201
    public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
202 22325
    {
203 504
        if ($indexName === null) {
204
            $indexName = $this->_generateIdentifierName(
205
                array_merge([$this->getName()], $columnNames),
206 22325
                'uniq',
207 407
                $this->_getMaxIdentifierLength()
208
            );
209
        }
210 22323
211 380
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
212
    }
213
214 22321
    /**
215
     * Renames an index.
216 22321
     *
217 488
     * @param string      $oldIndexName The name of the index to rename from.
218
     * @param string|null $newIndexName The name of the index to rename to.
219 488
     *                                  If null is given, the index name will be auto-generated.
220
     *
221
     * @return self This table instance.
222 22321
     *
223
     * @throws SchemaException If no index exists for the given current name
224 22321
     *                         or if an index with the given new name already exists on this table.
225 490
     */
226
    public function renameIndex($oldIndexName, $newIndexName = null)
227
    {
228 22319
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
229
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
230
231
        if ($oldIndexName === $normalizedNewIndexName) {
232
            return $this;
233
        }
234
235
        if (! $this->hasIndex($oldIndexName)) {
236
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
237
        }
238 22519
239
        if ($this->hasIndex($normalizedNewIndexName)) {
240 22519
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
241
        }
242 22519
243 22519
        $oldIndex = $this->_indexes[$oldIndexName];
244
245
        if ($oldIndex->isPrimary()) {
246
            $this->dropPrimaryKey();
247
248
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
249
        }
250
251
        unset($this->_indexes[$oldIndexName]);
252
253
        if ($oldIndex->isUnique()) {
254
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
255
        }
256
257
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
258
    }
259
260
    /**
261
     * Checks if an index begins in the order of the given columns.
262 25582
     *
263
     * @param string[] $columnNames
264 25582
     *
265 1244
     * @return bool
266
     */
267
    public function columnsAreIndexed(array $columnNames)
268 25580
    {
269 25578
        foreach ($this->getIndexes() as $index) {
270 2211
            /** @var $index Index */
271
            if ($index->spansColumns($columnNames)) {
272
                return true;
273
            }
274 25578
        }
275
276
        return false;
277
    }
278
279
    /**
280
     * @param string  $columnName
281
     * @param string  $typeName
282
     * @param mixed[] $options
283
     *
284 26002
     * @return Column
285
     */
286 26002
    public function addColumn($columnName, $typeName, array $options = [])
287
    {
288 26002
        $column = new Column($columnName, Type::getType($typeName), $options);
289
290 26002
        $this->_addColumn($column);
291
292
        return $column;
293
    }
294
295
    /**
296
     * Renames a Column.
297
     *
298
     * @deprecated
299
     *
300
     * @param string $oldColumnName
301
     * @param string $newColumnName
302
     *
303
     * @throws DBALException
304
     */
305
    public function renameColumn($oldColumnName, $newColumnName)
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

305
    public function renameColumn(/** @scrutinizer ignore-unused */ $oldColumnName, $newColumnName)

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

305
    public function renameColumn($oldColumnName, /** @scrutinizer ignore-unused */ $newColumnName)

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...
306
    {
307
        throw new DBALException(
308
            'Table#renameColumn() was removed, because it drops and recreates the column instead. ' .
309
            'There is no fix available, because a schema diff cannot reliably detect if a column ' .
310
            'was renamed or one column was created and another one dropped.'
311
        );
312
    }
313
314
    /**
315
     * Change Column Details.
316
     *
317
     * @param string  $columnName
318 22134
     * @param mixed[] $options
319
     *
320 22134
     * @return self
321 22134
     */
322
    public function changeColumn($columnName, array $options)
323 22134
    {
324
        $column = $this->getColumn($columnName);
325
326
        $column->setOptions($options);
327
328
        return $this;
329
    }
330
331
    /**
332
     * Drops a Column from the Table.
333 1638
     *
334
     * @param string $columnName
335 1638
     *
336 1638
     * @return self
337
     */
338 1638
    public function dropColumn($columnName)
339
    {
340
        $columnName = $this->normalizeIdentifier($columnName);
341
342
        unset($this->_columns[$columnName]);
343
344
        return $this;
345
    }
346
347
    /**
348
     * Adds a foreign key constraint.
349
     *
350
     * Name is inferred from the local columns.
351
     *
352
     * @param Table|string $foreignTable       Table schema instance or table name
353
     * @param string[]     $localColumnNames
354 24470
     * @param string[]     $foreignColumnNames
355
     * @param mixed[]      $options
356 24470
     * @param string|null  $constraintName
357
     *
358 24470
     * @return self
359
     */
360
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null)
361
    {
362
        if (! $constraintName) {
363
            $constraintName = $this->_generateIdentifierName(
364
                array_merge((array) $this->getName(), $localColumnNames),
365
                'fk',
366
                $this->_getMaxIdentifierLength()
367
            );
368
        }
369
370
        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

370
        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...
371
    }
372
373
    /**
374
     * Adds a foreign key constraint.
375 17119
     *
376
     * Name is to be generated by the database itself.
377 17119
     *
378
     * @deprecated Use {@link addForeignKeyConstraint}
379
     *
380
     * @param Table|string $foreignTable       Table schema instance or table name
381
     * @param string[]     $localColumnNames
382
     * @param string[]     $foreignColumnNames
383
     * @param mixed[]      $options
384
     *
385
     * @return self
386
     */
387
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
388
    {
389
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
390
    }
391
392
    /**
393
     * Adds a foreign key constraint with a given name.
394
     *
395 24472
     * @deprecated Use {@link addForeignKeyConstraint}
396
     *
397 24472
     * @param string       $name
398 24018
     * @param Table|string $foreignTable       Table schema instance or table name
399 24018
     * @param string[]     $localColumnNames
400 1250
     * @param string[]     $foreignColumnNames
401
     * @param mixed[]      $options
402
     *
403
     * @return self
404
     *
405 24470
     * @throws SchemaException
406 24470
     */
407 1387
    public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
408
    {
409
        if ($foreignTable instanceof Table) {
410
            foreach ($foreignColumnNames as $columnName) {
411 24468
                if (! $foreignTable->hasColumn($columnName)) {
412 24468
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
413 224
                }
414 224
            }
415 224
        }
416 224
417
        foreach ($localColumnNames as $columnName) {
418 24468
            if (! $this->hasColumn($columnName)) {
419
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
420 24468
            }
421
        }
422
423
        $constraint = new ForeignKeyConstraint(
424
            $localColumnNames,
425
            $foreignTable,
426
            $foreignColumnNames,
427
            $name,
428
            $options
429 22119
        );
430
431 22119
        return $this->_addForeignKeyConstraint($constraint);
432
    }
433 22119
434
    /**
435
     * @param string $name
436
     * @param mixed  $value
437
     *
438
     * @return self
439
     */
440
    public function addOption($name, $value)
441 26074
    {
442
        $this->_options[$name] = $value;
443 26074
444 26074
        return $this;
445
    }
446 26074
447 1568
    /**
448
     * Returns whether this table has a foreign key constraint with the given name.
449
     *
450 26074
     * @param string $constraintName
451 26074
     *
452
     * @return bool
453
     */
454
    public function hasForeignKey($constraintName)
455
    {
456
        $constraintName = $this->normalizeIdentifier($constraintName);
457
458
        return isset($this->_fkConstraints[$constraintName]);
459
    }
460 25592
461
    /**
462 25592
     * Returns the foreign key constraint with the given name.
463 25592
     *
464 25592
     * @param string $constraintName The constraint name.
465
     *
466 25592
     * @return ForeignKeyConstraint
467 20451
     *
468 20443
     * @throws SchemaException If the foreign key does not exist.
469
     */
470
    public function getForeignKey($constraintName)
471 818
    {
472
        $constraintName = $this->normalizeIdentifier($constraintName);
473
474 25592
        if (! $this->hasForeignKey($constraintName)) {
475 25592
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
476
        }
477 1435
478
        return $this->_fkConstraints[$constraintName];
479
    }
480 25592
481 818
    /**
482
     * Removes the foreign key constraint with the given name.
483
     *
484 25592
     * @param string $constraintName The constraint name.
485 25288
     *
486
     * @return void
487
     *
488 25592
     * @throws SchemaException
489
     */
490 25592
    public function removeForeignKey($constraintName)
491
    {
492
        $constraintName = $this->normalizeIdentifier($constraintName);
493
494
        if (! $this->hasForeignKey($constraintName)) {
495
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
496 24478
        }
497
498 24478
        unset($this->_fkConstraints[$constraintName]);
499
    }
500 24478
501 24476
    /**
502
     * Returns whether this table has a unique constraint with the given name.
503 1379
     *
504 1379
     * @param string $constraintName
505 1379
     *
506 1379
     * @return bool
507
     */
508
    public function hasUniqueConstraint($constraintName)
509 24478
    {
510
        $constraintName = $this->normalizeIdentifier($constraintName);
511 24478
512
        return isset($this->_uniqueConstraints[$constraintName]);
513
    }
514
515
    /**
516 24478
     * Returns the unique constraint with the given name.
517 24478
     *
518 24478
     * @param string $constraintName The constraint name.
519 24478
     *
520
     * @return UniqueConstraint
521 24478
     *
522
     * @throws SchemaException If the foreign key does not exist.
523 24478
     */
524 24426
    public function getUniqueConstraint($constraintName)
525 22882
    {
526
        $constraintName = $this->normalizeIdentifier($constraintName);
527
528
        if (! $this->hasUniqueConstraint($constraintName)) {
529 24418
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
530 24418
        }
531 24418
532
        return $this->_uniqueConstraints[$constraintName];
533
    }
534
535
    /**
536
     * Removes the unique constraint with the given name.
537
     *
538
     * @param string $constraintName The constraint name.
539
     *
540 22321
     * @return void
541
     *
542 22321
     * @throws SchemaException
543
     */
544 22321
    public function removeUniqueConstraint($constraintName)
545
    {
546
        $constraintName = $this->normalizeIdentifier($constraintName);
547
548
        if (! $this->hasUniqueConstraint($constraintName)) {
549
            throw SchemaException::uniqueConstraintDoesNotExist($constraintName, $this->_name);
550
        }
551
552
        unset($this->_uniqueConstraints[$constraintName]);
553
    }
554
555
    /**
556 369
     * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
557
     *
558 369
     * @return Column[]
559 369
     */
560
    public function getColumns()
561
    {
562
        $columns = $this->_columns;
563 369
        $pkCols  = [];
564
        $fkCols  = [];
565
566
        $primaryKey = $this->getPrimaryKey();
567
568
        if ($primaryKey !== null) {
569
            $pkCols = $primaryKey->getColumns();
570
        }
571
572
        foreach ($this->getForeignKeys() as $fk) {
573
            /** @var ForeignKeyConstraint $fk */
574
            $fkCols = array_merge($fkCols, $fk->getColumns());
575 371
        }
576
577 371
        $colNames = array_unique(array_merge($pkCols, $fkCols, array_keys($columns)));
578 371
579
        uksort($columns, static function ($a, $b) use ($colNames) {
580
            return array_search($a, $colNames) >= array_search($b, $colNames);
581
        });
582 371
583 371
        return $columns;
584
    }
585
586
    /**
587
     * Returns whether this table has a Column with the given name.
588
     *
589
     * @param string $columnName The column name.
590 25656
     *
591
     * @return bool
592 25656
     */
593 25656
    public function hasColumn($columnName)
594
    {
595 25656
        $columnName = $this->normalizeIdentifier($columnName);
596 25048
597
        return isset($this->_columns[$columnName]);
598
    }
599 25656
600
    /**
601
     * Returns the Column with the given name.
602
     *
603
     * @param string $columnName The column name.
604
     *
605
     * @return Column
606
     *
607 25656
     * @throws SchemaException If the column does not exist.
608
     */
609 25656
    public function getColumn($columnName)
610 25656
    {
611 24354
        $columnName = $this->normalizeIdentifier($columnName);
612
613
        if (! $this->hasColumn($columnName)) {
614 25656
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
615
        }
616
617
        return $this->_columns[$columnName];
618
    }
619
620
    /**
621
     * Returns the primary key.
622
     *
623
     * @return Index|null The primary key, or null if this Table has no primary key.
624 25656
     */
625
    public function getPrimaryKey()
626
    {
627 25604
        return $this->hasPrimaryKey()
628 25656
            ? $this->getIndex($this->_primaryKeyName)
629
            : null;
630
    }
631
632
    /**
633
     * Returns the primary key columns.
634
     *
635
     * @return string[]
636
     *
637
     * @throws DBALException
638 25746
     */
639
    public function getPrimaryKeyColumns()
640 25746
    {
641
        $primaryKey = $this->getPrimaryKey();
642 25746
643
        if ($primaryKey === null) {
644
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
645
        }
646
647
        return $primaryKey->getColumns();
648
    }
649
650
    /**
651
     * Returns whether this table has a primary key.
652
     *
653
     * @return bool
654 25478
     */
655
    public function hasPrimaryKey()
656 25478
    {
657 25478
        return $this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName);
658 1595
    }
659
660
    /**
661 25476
     * Returns whether this table has an Index with the given name.
662
     *
663
     * @param string $indexName The index name.
664
     *
665
     * @return bool
666
     */
667
    public function hasIndex($indexName)
668
    {
669 25666
        $indexName = $this->normalizeIdentifier($indexName);
670
671 25666
        return isset($this->_indexes[$indexName]);
672 24859
    }
673
674
    /**
675 25058
     * Returns the Index with the given name.
676
     *
677
     * @param string $indexName The index name.
678
     *
679
     * @return Index
680
     *
681
     * @throws SchemaException If the index does not exist.
682
     */
683
    public function getIndex($indexName)
684
    {
685 22061
        $indexName = $this->normalizeIdentifier($indexName);
686
687 22061
        if (! $this->hasIndex($indexName)) {
688
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
689 22061
        }
690
691
        return $this->_indexes[$indexName];
692
    }
693 22061
694
    /**
695
     * @return Index[]
696
     */
697
    public function getIndexes()
698
    {
699
        return $this->_indexes;
700
    }
701 25672
702
    /**
703 25672
     * Returns the unique constraints.
704
     *
705
     * @return UniqueConstraint[]
706
     */
707
    public function getUniqueConstraints()
708
    {
709
        return $this->_uniqueConstraints;
710
    }
711
712
    /**
713 25162
     * Returns the foreign key constraints.
714
     *
715 25162
     * @return ForeignKeyConstraint[]
716
     */
717 25162
    public function getForeignKeys()
718
    {
719
        return $this->_fkConstraints;
720
    }
721
722
    /**
723
     * @param string $name
724
     *
725
     * @return bool
726
     */
727
    public function hasOption($name)
728
    {
729 25118
        return isset($this->_options[$name]);
730
    }
731 25118
732 25118
    /**
733 1460
     * @param string $name
734
     *
735
     * @return mixed
736 25116
     */
737
    public function getOption($name)
738
    {
739
        return $this->_options[$name];
740
    }
741
742 25600
    /**
743
     * @return mixed[]
744 25600
     */
745
    public function getOptions()
746
    {
747
        return $this->_options;
748
    }
749
750
    /**
751
     * @return void
752 25692
     */
753
    public function visit(Visitor $visitor)
754 25692
    {
755
        $visitor->acceptTable($this);
756
757
        foreach ($this->getColumns() as $column) {
758
            $visitor->acceptColumn($this, $column);
759
        }
760
761
        foreach ($this->getIndexes() as $index) {
762 22221
            $visitor->acceptIndex($this, $index);
763
        }
764 22221
765
        foreach ($this->getForeignKeys() as $constraint) {
766
            $visitor->acceptForeignKey($this, $constraint);
767
        }
768
    }
769
770
    /**
771
     * Clone of a Table triggers a deep clone of all affected assets.
772 21660
     *
773
     * @return void
774 21660
     */
775
    public function __clone()
776
    {
777
        foreach ($this->_columns as $k => $column) {
778
            $this->_columns[$k] = clone $column;
779
        }
780 25386
781
        foreach ($this->_indexes as $k => $index) {
782 25386
            $this->_indexes[$k] = clone $index;
783
        }
784
785
        foreach ($this->_fkConstraints as $k => $fk) {
786
            $this->_fkConstraints[$k] = clone $fk;
787
            $this->_fkConstraints[$k]->setLocalTable($this);
788 24036
        }
789
    }
790 24036
791
    /**
792 24036
     * @return int
793 24030
     */
794
    protected function _getMaxIdentifierLength()
795
    {
796 24036
        return $this->_schemaConfig instanceof SchemaConfig
797 21875
            ? $this->_schemaConfig->getMaxIdentifierLength()
798
            : 63;
799
    }
800 24036
801 170
    /**
802
     * @return void
803 24036
     *
804
     * @throws SchemaException
805
     */
806
    protected function _addColumn(Column $column)
807
    {
808
        $columnName = $column->getName();
809
        $columnName = $this->normalizeIdentifier($columnName);
810 23373
811
        if (isset($this->_columns[$columnName])) {
812 23373
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
813 23371
        }
814
815 23373
        $this->_columns[$columnName] = $column;
816 23349
    }
817
818 23373
    /**
819 22313
     * Adds an index to the table.
820 22313
     *
821
     * @return self
822 23373
     *
823
     * @throws SchemaException
824
     */
825
    protected function _addIndex(Index $indexCandidate)
826
    {
827
        $indexName               = $indexCandidate->getName();
828
        $indexName               = $this->normalizeIdentifier($indexName);
829
        $replacedImplicitIndexes = [];
830
831
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
832
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
833 26082
                continue;
834
            }
835 26082
836 488
            $replacedImplicitIndexes[] = $name;
837
        }
838
839 26082
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
840
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
841
        ) {
842
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
843
        }
844
845
        foreach ($replacedImplicitIndexes as $name) {
846
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
847
        }
848
849
        if ($indexCandidate->isPrimary()) {
850
            $this->_primaryKeyName = $indexName;
851
        }
852
853
        $this->_indexes[$indexName] = $indexCandidate;
854
855
        return $this;
856
    }
857
858
    /**
859
     * @return self
860
     */
861
    protected function _addUniqueConstraint(UniqueConstraint $constraint)
862
    {
863
        $name = strlen($constraint->getName())
864
            ? $constraint->getName()
865
            : $this->_generateIdentifierName(
866
                array_merge((array) $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

866
                array_merge((array) $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...
867
                'fk',
868
                $this->_getMaxIdentifierLength()
869
            );
870
871
        $name = $this->normalizeIdentifier($name);
872
873
        $this->_uniqueConstraints[$name] = $constraint;
874
875
        // If there is already an index that fulfills this requirements drop the request. In the case of __construct
876
        // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates.
877
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
878
        $indexName = $this->_generateIdentifierName(
879
            array_merge([$this->getName()], $constraint->getColumns()),
880
            'idx',
881
            $this->_getMaxIdentifierLength()
882
        );
883
884
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, true, false);
885
886
        foreach ($this->_indexes as $existingIndex) {
887
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
888
                return $this;
889
            }
890
        }
891
892
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
893
894
        return $this;
895
    }
896
897
    /**
898
     * @return self
899
     */
900
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
901
    {
902
        $constraint->setLocalTable($this);
903
904
        $name = strlen($constraint->getName())
905
            ? $constraint->getName()
906
            : $this->_generateIdentifierName(
907
                array_merge((array) $this->getName(), $constraint->getLocalColumns()),
908
                'fk',
909
                $this->_getMaxIdentifierLength()
910
            );
911
912
        $name = $this->normalizeIdentifier($name);
913
914
        $this->_fkConstraints[$name] = $constraint;
915
916
        // add an explicit index on the foreign key columns.
917
        // If there is already an index that fulfills this requirements drop the request. In the case of __construct
918
        // calling this method during hydration from schema-details all the explicitly added indexes lead to duplicates.
919
        // This creates computation overhead in this case, however no duplicate indexes are ever added (column based).
920
        $indexName = $this->_generateIdentifierName(
921
            array_merge([$this->getName()], $constraint->getColumns()),
922
            'idx',
923
            $this->_getMaxIdentifierLength()
924
        );
925
926
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
927
928
        foreach ($this->_indexes as $existingIndex) {
929
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
930
                return $this;
931
            }
932
        }
933
934
        $this->_addIndex($indexCandidate);
935
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
936
937
        return $this;
938
    }
939
940
    /**
941
     * Normalizes a given identifier.
942
     *
943
     * Trims quotes and lowercases the given identifier.
944
     *
945
     * @param string|null $identifier The identifier to normalize.
946
     *
947
     * @return string The normalized identifier.
948
     */
949
    private function normalizeIdentifier($identifier)
950
    {
951
        if ($identifier === null) {
952
            return '';
953
        }
954
955
        return $this->trimQuotes(strtolower($identifier));
956
    }
957
958
    /**
959
     * @param mixed[] $columns
960
     * @param string  $indexName
961
     * @param mixed[] $flags
962
     * @param mixed[] $options
963
     *
964
     * @return UniqueConstraint
965
     *
966
     * @throws SchemaException
967
     */
968
    private function _createUniqueConstraint(array $columns, $indexName, array $flags = [], array $options = [])
969
    {
970
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
971
            throw SchemaException::indexNameInvalid($indexName);
972
        }
973
974
        foreach ($columns as $index => $value) {
975
            if (is_string($index)) {
976
                $columnName = $index;
977
            } else {
978
                $columnName = $value;
979
            }
980
981
            if (! $this->hasColumn($columnName)) {
982
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
983
            }
984
        }
985
986
        return new UniqueConstraint($indexName, $columns, $flags, $options);
987
    }
988
989
    /**
990
     * @param mixed[]  $columns
991
     * @param string   $indexName
992
     * @param bool     $isUnique
993
     * @param bool     $isPrimary
994
     * @param string[] $flags
995
     * @param mixed[]  $options
996
     *
997
     * @return Index
998
     *
999
     * @throws SchemaException
1000
     */
1001
    private function _createIndex(array $columns, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
1002
    {
1003
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
1004
            throw SchemaException::indexNameInvalid($indexName);
1005
        }
1006
1007
        foreach ($columns as $index => $value) {
1008
            if (is_string($index)) {
1009
                $columnName = $index;
1010
            } else {
1011
                $columnName = $value;
1012
            }
1013
1014
            if (! $this->hasColumn($columnName)) {
1015
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
1016
            }
1017
        }
1018
1019
        return new Index($indexName, $columns, $isUnique, $isPrimary, $flags, $options);
1020
    }
1021
}
1022