Failed Conditions
Push — develop ( c067f0...c4478a )
by Sergei
10:16
created

Table::getColumns()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 22
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 3

Importance

Changes 0
Metric Value
eloc 11
dl 0
loc 22
ccs 12
cts 12
cp 1
rs 9.9
c 0
b 0
f 0
cc 3
nc 4
nop 0
crap 3
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
 * @link   www.doctrine-project.org
24
 */
25
class Table extends AbstractAsset
26
{
27
    /** @var string */
28
    protected $_name = null;
29
30
    /** @var Column[] */
31
    protected $_columns = [];
32
33
    /** @var Index[] */
34
    private $implicitIndexes = [];
35
36
    /** @var Index[] */
37
    protected $_indexes = [];
38
39
    /** @var string */
40
    protected $_primaryKeyName = false;
41
42
    /** @var UniqueConstraint[] */
43
    protected $_uniqueConstraints = [];
44
45
    /** @var ForeignKeyConstraint[] */
46
    protected $_fkConstraints = [];
47
48
    /** @var mixed[] */
49
    protected $_options = [];
50
51
    /** @var SchemaConfig|null */
52
    protected $_schemaConfig = null;
53
54
    /**
55
     * @param string                 $tableName
56
     * @param Column[]               $columns
57
     * @param Index[]                $indexes
58
     * @param UniqueConstraint[]     $uniqueConstraints
59
     * @param ForeignKeyConstraint[] $fkConstraints
60
     * @param mixed[]                $options
61
     *
62
     * @throws DBALException
63
     */
64 13094
    public function __construct(
65
        $tableName,
66
        array $columns = [],
67
        array $indexes = [],
68
        array $uniqueConstraints = [],
69
        array $fkConstraints = [],
70
        array $options = []
71
    ) {
72 13094
        if (strlen($tableName) === 0) {
73 19
            throw DBALException::invalidTableName($tableName);
74
        }
75
76 13075
        $this->_setName($tableName);
77
78 13075
        foreach ($columns as $column) {
79 1333
            $this->_addColumn($column);
80
        }
81
82 13056
        foreach ($indexes as $idx) {
83 535
            $this->_addIndex($idx);
84
        }
85
86 13018
        foreach ($uniqueConstraints as $uniqueConstraint) {
87
            $this->_addUniqueConstraint($uniqueConstraint);
88
        }
89
90 13018
        foreach ($fkConstraints as $fkConstraint) {
91 216
            $this->_addForeignKeyConstraint($fkConstraint);
92
        }
93
94 13018
        $this->_options = $options;
95 13018
    }
96
97
    /**
98
     * @return void
99
     */
100 1262
    public function setSchemaConfig(SchemaConfig $schemaConfig)
101
    {
102 1262
        $this->_schemaConfig = $schemaConfig;
103 1262
    }
104
105
    /**
106
     * Sets the Primary Key.
107
     *
108
     * @param mixed[][]   $columns
109
     * @param string|bool $indexName
110
     *
111
     * @return self
112
     */
113 5449
    public function setPrimaryKey(array $columns, $indexName = false)
114
    {
115 5449
        $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

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

309
    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

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

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

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