Completed
Push — develop ( fa42c1...0ef7d4 )
by Sergei
22:52
created

Table::getIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
eloc 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 in_array;
13
use function is_numeric;
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 string */
26
    protected $_name = null;
27
28
    /** @var Column[] */
29
    protected $_columns = [];
30
31
    /** @var Index[] */
32
    private $implicitIndexes = [];
33
34
    /** @var Index[] */
35
    protected $_indexes = [];
36
37
    /** @var string */
38
    protected $_primaryKeyName = false;
39
40
    /** @var UniqueConstraint[] */
41
    protected $_uniqueConstraints = [];
42
43
    /** @var ForeignKeyConstraint[] */
44
    protected $_fkConstraints = [];
45
46
    /** @var mixed[] */
47
    protected $_options = [];
48
49
    /** @var SchemaConfig|null */
50
    protected $_schemaConfig = null;
51
52
    /**
53
     * @param string                 $tableName
54
     * @param Column[]               $columns
55
     * @param Index[]                $indexes
56
     * @param UniqueConstraint[]     $uniqueConstraints
57
     * @param ForeignKeyConstraint[] $fkConstraints
58
     * @param mixed[]                $options
59
     *
60
     * @throws DBALException
61
     */
62 16026
    public function __construct(
63
        $tableName,
64
        array $columns = [],
65
        array $indexes = [],
66
        array $uniqueConstraints = [],
67
        array $fkConstraints = [],
68
        array $options = []
69
    ) {
70 16026
        if (strlen($tableName) === 0) {
71 23
            throw DBALException::invalidTableName($tableName);
72
        }
73
74 16003
        $this->_setName($tableName);
75
76 16003
        foreach ($columns as $column) {
77 1747
            $this->_addColumn($column);
78
        }
79
80 15980
        foreach ($indexes as $idx) {
81 670
            $this->_addIndex($idx);
82
        }
83
84 15934
        foreach ($uniqueConstraints as $uniqueConstraint) {
85
            $this->_addUniqueConstraint($uniqueConstraint);
86
        }
87
88 15934
        foreach ($fkConstraints as $fkConstraint) {
89 264
            $this->_addForeignKeyConstraint($fkConstraint);
90
        }
91
92 15934
        $this->_options = $options;
93 15934
    }
94
95
    /**
96
     * @return void
97
     */
98 1534
    public function setSchemaConfig(SchemaConfig $schemaConfig)
99
    {
100 1534
        $this->_schemaConfig = $schemaConfig;
101 1534
    }
102
103
    /**
104
     * Sets the Primary Key.
105
     *
106
     * @param mixed[][]   $columns
107
     * @param string|bool $indexName
108
     *
109
     * @return self
110
     */
111 6625
    public function setPrimaryKey(array $columns, $indexName = false)
112
    {
113 6625
        $this->_addIndex($this->_createIndex($columns, $indexName ?: 'primary', true, true));
0 ignored issues
show
Bug introduced by
It seems like $indexName ?: 'primary' can also be of type true; however, parameter $indexName of Doctrine\DBAL\Schema\Table::_createIndex() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

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

113
        $this->_addIndex($this->_createIndex($columns, /** @scrutinizer ignore-type */ $indexName ?: 'primary', true, true));
Loading history...
114
115 6625
        foreach ($columns as $columnName) {
116 6625
            $column = $this->getColumn($columnName);
117 6625
            $column->setNotnull(true);
118
        }
119
120 6625
        return $this;
121
    }
122
123
    /**
124
     * @param mixed[]     $columnNames
125
     * @param string|null $indexName
126
     * @param string[]    $flags
127
     * @param mixed[]     $options
128
     *
129
     * @return self
130
     */
131
    public function addUniqueConstraint(array $columnNames, $indexName = null, array $flags = [], array $options = [])
132
    {
133
        if ($indexName === null) {
134
            $indexName = $this->_generateIdentifierName(
135
                array_merge([$this->getName()], $columnNames),
136
                'uniq',
137
                $this->_getMaxIdentifierLength()
138
            );
139
        }
140
141
        return $this->_addUniqueConstraint($this->_createUniqueConstraint($columnNames, $indexName, $flags, $options));
142
    }
143
144
    /**
145
     * @param mixed[][]   $columnNames
146
     * @param string|null $indexName
147
     * @param string[]    $flags
148
     * @param mixed[]     $options
149
     *
150
     * @return self
151
     */
152 2044
    public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
153
    {
154 2044
        if ($indexName === null) {
155 457
            $indexName = $this->_generateIdentifierName(
156 457
                array_merge([$this->getName()], $columnNames),
157 457
                'idx',
158 457
                $this->_getMaxIdentifierLength()
159
            );
160
        }
161
162 2044
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
163
    }
164
165
    /**
166
     * Drops the primary key from this table.
167
     *
168
     * @return void
169
     */
170 426
    public function dropPrimaryKey()
171
    {
172 426
        $this->dropIndex($this->_primaryKeyName);
173 426
        $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...
174 426
    }
175
176
    /**
177
     * Drops an index from this table.
178
     *
179
     * @param string $indexName The index name.
180
     *
181
     * @return void
182
     *
183
     * @throws SchemaException If the index does not exist.
184
     */
185 737
    public function dropIndex($indexName)
186
    {
187 737
        $indexName = $this->normalizeIdentifier($indexName);
188
189 737
        if (! $this->hasIndex($indexName)) {
190
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
191
        }
192
193 737
        unset($this->_indexes[$indexName]);
194 737
    }
195
196
    /**
197
     * @param mixed[][]   $columnNames
198
     * @param string|null $indexName
199
     * @param mixed[]     $options
200
     *
201
     * @return self
202
     */
203 614
    public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
204
    {
205 614
        if ($indexName === null) {
206 423
            $indexName = $this->_generateIdentifierName(
207 423
                array_merge([$this->getName()], $columnNames),
208 423
                'uniq',
209 423
                $this->_getMaxIdentifierLength()
210
            );
211
        }
212
213 614
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
214
    }
215
216
    /**
217
     * Renames an index.
218
     *
219
     * @param string      $oldIndexName The name of the index to rename from.
220
     * @param string|null $newIndexName The name of the index to rename to.
221
     *                                  If null is given, the index name will be auto-generated.
222
     *
223
     * @return self This table instance.
224
     *
225
     * @throws SchemaException If no index exists for the given current name
226
     *                         or if an index with the given new name already exists on this table.
227
     */
228 321
    public function renameIndex($oldIndexName, $newIndexName = null)
229
    {
230 321
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
231 321
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
232
233 321
        if ($oldIndexName === $normalizedNewIndexName) {
234 207
            return $this;
235
        }
236
237 321
        if (! $this->hasIndex($oldIndexName)) {
238 23
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
239
        }
240
241 298
        if ($this->hasIndex($normalizedNewIndexName)) {
242 23
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
243
        }
244
245 275
        $oldIndex = $this->_indexes[$oldIndexName];
246
247 275
        if ($oldIndex->isPrimary()) {
248 23
            $this->dropPrimaryKey();
249
250 23
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName);
251
        }
252
253 275
        unset($this->_indexes[$oldIndexName]);
254
255 275
        if ($oldIndex->isUnique()) {
256 46
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
257
        }
258
259 252
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
260
    }
261
262
    /**
263
     * Checks if an index begins in the order of the given columns.
264
     *
265
     * @param mixed[][] $columnsNames
266
     *
267
     * @return bool
268
     */
269 45
    public function columnsAreIndexed(array $columnsNames)
270
    {
271 45
        foreach ($this->getIndexes() as $index) {
272
            /** @var $index Index */
273 45
            if ($index->spansColumns($columnsNames)) {
274 45
                return true;
275
            }
276
        }
277
278
        return false;
279
    }
280
281
    /**
282
     * @param string  $columnName
283
     * @param string  $typeName
284
     * @param mixed[] $options
285
     *
286
     * @return Column
287
     */
288 13217
    public function addColumn($columnName, $typeName, array $options = [])
289
    {
290 13217
        $column = new Column($columnName, Type::getType($typeName), $options);
291
292 13217
        $this->_addColumn($column);
293
294 13217
        return $column;
295
    }
296
297
    /**
298
     * Renames a Column.
299
     *
300
     * @deprecated
301
     *
302
     * @param string $oldColumnName
303
     * @param string $newColumnName
304
     *
305
     * @throws DBALException
306
     */
307
    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

307
    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

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

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

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