Completed
Push — master ( 292d81...c07ed1 )
by Sergei
47:58 queued 44:41
created

Table::visit()   A

Complexity

Conditions 4
Paths 8

Size

Total Lines 14
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 4

Importance

Changes 0
Metric Value
eloc 7
dl 0
loc 14
ccs 8
cts 8
cp 1
rs 10
c 0
b 0
f 0
cc 4
nc 8
nop 1
crap 4
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 const ARRAY_FILTER_USE_KEY;
9
use function array_filter;
10
use function array_merge;
11
use function in_array;
12
use function preg_match;
13
use function strlen;
14
use function strtolower;
15
16
/**
17
 * Object Representation of a table.
18
 */
19
class Table extends AbstractAsset
20
{
21
    /** @var Column[] */
22
    protected $_columns = [];
23
24
    /** @var Index[] */
25
    private $implicitIndexes = [];
26
27
    /** @var Index[] */
28
    protected $_indexes = [];
29
30
    /** @var string */
31
    protected $_primaryKeyName = false;
32
33
    /** @var ForeignKeyConstraint[] */
34
    protected $_fkConstraints = [];
35
36
    /** @var mixed[] */
37
    protected $_options = [
38
        'create_options' => [],
39
    ];
40
41
    /** @var SchemaConfig|null */
42
    protected $_schemaConfig = null;
43
44
    /**
45
     * @param string                 $tableName
46
     * @param Column[]               $columns
47
     * @param Index[]                $indexes
48
     * @param ForeignKeyConstraint[] $fkConstraints
49
     * @param int                    $idGeneratorType
50
     * @param mixed[]                $options
51
     *
52
     * @throws DBALException
53
     */
54 24643
    public function __construct($tableName, array $columns = [], array $indexes = [], array $fkConstraints = [], $idGeneratorType = 0, array $options = [])
0 ignored issues
show
Unused Code introduced by
The parameter $idGeneratorType 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

54
    public function __construct($tableName, array $columns = [], array $indexes = [], array $fkConstraints = [], /** @scrutinizer ignore-unused */ $idGeneratorType = 0, array $options = [])

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...
55
    {
56 24643
        if (strlen($tableName) === 0) {
57 1531
            throw DBALException::invalidTableName($tableName);
58
        }
59
60 24641
        $this->_setName($tableName);
61
62 24641
        foreach ($columns as $column) {
63 22123
            $this->_addColumn($column);
64
        }
65
66 24639
        foreach ($indexes as $idx) {
67 21885
            $this->_addIndex($idx);
68
        }
69
70 24635
        foreach ($fkConstraints as $constraint) {
71 20978
            $this->_addForeignKeyConstraint($constraint);
72
        }
73
74 24635
        $this->_options = array_merge($this->_options, $options);
75 24635
    }
76
77
    /**
78
     * @return void
79
     */
80 22606
    public function setSchemaConfig(SchemaConfig $schemaConfig)
81
    {
82 22606
        $this->_schemaConfig = $schemaConfig;
83 22606
    }
84
85
    /**
86
     * @return int
87
     */
88 22867
    protected function _getMaxIdentifierLength()
89
    {
90 22867
        if ($this->_schemaConfig instanceof SchemaConfig) {
91 22402
            return $this->_schemaConfig->getMaxIdentifierLength();
92
        }
93
94 22801
        return 63;
95
    }
96
97
    /**
98
     * Sets the Primary Key.
99
     *
100
     * @param string[]     $columnNames
101
     * @param string|false $indexName
102
     *
103
     * @return self
104
     */
105 23567
    public function setPrimaryKey(array $columnNames, $indexName = false)
106
    {
107 23567
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
108
109 23567
        foreach ($columnNames as $columnName) {
110 23567
            $column = $this->getColumn($columnName);
111 23567
            $column->setNotnull(true);
112
        }
113
114 23567
        return $this;
115
    }
116
117
    /**
118
     * @param string[]    $columnNames
119
     * @param string|null $indexName
120
     * @param string[]    $flags
121
     * @param mixed[]     $options
122
     *
123
     * @return self
124
     */
125 21436
    public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
126
    {
127 21436
        if ($indexName === null) {
128 20909
            $indexName = $this->_generateIdentifierName(
129 20909
                array_merge([$this->getName()], $columnNames),
130 20909
                'idx',
131 20909
                $this->_getMaxIdentifierLength()
132
            );
133
        }
134
135 21436
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, false, false, $flags, $options));
136
    }
137
138
    /**
139
     * Drops the primary key from this table.
140
     *
141
     * @return void
142
     */
143 20153
    public function dropPrimaryKey()
144
    {
145 20153
        $this->dropIndex($this->_primaryKeyName);
146 20153
        $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...
147 20153
    }
148
149
    /**
150
     * Drops an index from this table.
151
     *
152
     * @param string $indexName The index name.
153
     *
154
     * @return void
155
     *
156
     * @throws SchemaException If the index does not exist.
157
     */
158 20190
    public function dropIndex($indexName)
159
    {
160 20190
        $indexName = $this->normalizeIdentifier($indexName);
161 20190
        if (! $this->hasIndex($indexName)) {
162
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
163
        }
164 20190
        unset($this->_indexes[$indexName]);
165 20190
    }
166
167
    /**
168
     * @param string[]    $columnNames
169
     * @param string|null $indexName
170
     * @param mixed[]     $options
171
     *
172
     * @return self
173
     */
174 22460
    public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
175
    {
176 22460
        if ($indexName === null) {
177 22104
            $indexName = $this->_generateIdentifierName(
178 22104
                array_merge([$this->getName()], $columnNames),
179 22104
                'uniq',
180 22104
                $this->_getMaxIdentifierLength()
181
            );
182
        }
183
184 22460
        return $this->_addIndex($this->_createIndex($columnNames, $indexName, true, false, [], $options));
185
    }
186
187
    /**
188
     * Renames an index.
189
     *
190
     * @param string      $oldIndexName The name of the index to rename from.
191
     * @param string|null $newIndexName The name of the index to rename to.
192
     *                                  If null is given, the index name will be auto-generated.
193
     *
194
     * @return self This table instance.
195
     *
196
     * @throws SchemaException If no index exists for the given current name
197
     *                         or if an index with the given new name already exists on this table.
198
     */
199 19799
    public function renameIndex($oldIndexName, $newIndexName = null)
200
    {
201 19799
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
202 19799
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
203
204 19799
        if ($oldIndexName === $normalizedNewIndexName) {
205 397
            return $this;
206
        }
207
208 19799
        if (! $this->hasIndex($oldIndexName)) {
209 306
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
210
        }
211
212 19797
        if ($this->hasIndex($normalizedNewIndexName)) {
213 281
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
214
        }
215
216 19795
        $oldIndex = $this->_indexes[$oldIndexName];
217
218 19795
        if ($oldIndex->isPrimary()) {
219 381
            $this->dropPrimaryKey();
220
221 381
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
222
        }
223
224 19795
        unset($this->_indexes[$oldIndexName]);
225
226 19795
        if ($oldIndex->isUnique()) {
227 383
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
228
        }
229
230 19793
        return $this->addIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getFlags(), $oldIndex->getOptions());
231
    }
232
233
    /**
234
     * Checks if an index begins in the order of the given columns.
235
     *
236
     * @param string[] $columnNames
237
     *
238
     * @return bool
239
     */
240 19974
    public function columnsAreIndexed(array $columnNames)
241
    {
242 19974
        foreach ($this->getIndexes() as $index) {
243
            /** @var $index Index */
244 19974
            if ($index->spansColumns($columnNames)) {
245 19974
                return true;
246
            }
247
        }
248
249
        return false;
250
    }
251
252
    /**
253
     * @param string[] $columnNames
254
     * @param string   $indexName
255
     * @param bool     $isUnique
256
     * @param bool     $isPrimary
257
     * @param string[] $flags
258
     * @param mixed[]  $options
259
     *
260
     * @return Index
261
     *
262
     * @throws SchemaException
263
     */
264 23865
    private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
265
    {
266 23865
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
267 1056
            throw SchemaException::indexNameInvalid($indexName);
268
        }
269
270 23863
        foreach ($columnNames as $columnName) {
271 23861
            if (! $this->hasColumn($columnName)) {
272 2025
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
273
            }
274
        }
275
276 23861
        return new Index($indexName, $columnNames, $isUnique, $isPrimary, $flags, $options);
277
    }
278
279
    /**
280
     * @param string  $columnName
281
     * @param string  $typeName
282
     * @param mixed[] $options
283
     *
284
     * @return Column
285
     */
286 24291
    public function addColumn($columnName, $typeName, array $options = [])
287
    {
288 24291
        $column = new Column($columnName, Type::getType($typeName), $options);
289
290 24291
        $this->_addColumn($column);
291
292 24291
        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('Table#renameColumn() was removed, because it drops and recreates ' .
308
            'the column instead. There is no fix available, because a schema diff cannot reliably detect if a ' .
309
            'column was renamed or one column was created and another one dropped.');
310
    }
311
312
    /**
313
     * Change Column Details.
314
     *
315
     * @param string  $columnName
316
     * @param mixed[] $options
317
     *
318
     * @return self
319
     */
320 20424
    public function changeColumn($columnName, array $options)
321
    {
322 20424
        $column = $this->getColumn($columnName);
323 20424
        $column->setOptions($options);
324
325 20424
        return $this;
326
    }
327
328
    /**
329
     * Drops a Column from the Table.
330
     *
331
     * @param string $columnName
332
     *
333
     * @return self
334
     */
335 1422
    public function dropColumn($columnName)
336
    {
337 1422
        $columnName = $this->normalizeIdentifier($columnName);
338 1422
        unset($this->_columns[$columnName]);
339
340 1422
        return $this;
341
    }
342
343
    /**
344
     * Adds a foreign key constraint.
345
     *
346
     * Name is inferred from the local columns.
347
     *
348
     * @param Table|string $foreignTable       Table schema instance or table name
349
     * @param string[]     $localColumnNames
350
     * @param string[]     $foreignColumnNames
351
     * @param mixed[]      $options
352
     * @param string|null  $constraintName
353
     *
354
     * @return self
355
     */
356 22626
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null)
357
    {
358 22626
        $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), 'fk', $this->_getMaxIdentifierLength());
359
360 22626
        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

360
        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...
361
    }
362
363
    /**
364
     * Adds a foreign key constraint.
365
     *
366
     * Name is to be generated by the database itself.
367
     *
368
     * @deprecated Use {@link addForeignKeyConstraint}
369
     *
370
     * @param Table|string $foreignTable       Table schema instance or table name
371
     * @param string[]     $localColumnNames
372
     * @param string[]     $foreignColumnNames
373
     * @param mixed[]      $options
374
     *
375
     * @return self
376
     */
377 15925
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
378
    {
379 15925
        return $this->addForeignKeyConstraint($foreignTable, $localColumnNames, $foreignColumnNames, $options);
380
    }
381
382
    /**
383
     * Adds a foreign key constraint with a given name.
384
     *
385
     * @deprecated Use {@link addForeignKeyConstraint}
386
     *
387
     * @param string       $name
388
     * @param Table|string $foreignTable       Table schema instance or table name
389
     * @param string[]     $localColumnNames
390
     * @param string[]     $foreignColumnNames
391
     * @param mixed[]      $options
392
     *
393
     * @return self
394
     *
395
     * @throws SchemaException
396
     */
397 22628
    public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
398
    {
399 22628
        if ($foreignTable instanceof Table) {
400 22139
            foreach ($foreignColumnNames as $columnName) {
401 22139
                if (! $foreignTable->hasColumn($columnName)) {
402 1070
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
403
                }
404
            }
405
        }
406
407 22626
        foreach ($localColumnNames as $columnName) {
408 22626
            if (! $this->hasColumn($columnName)) {
409 1205
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
410
            }
411
        }
412
413 22624
        $constraint = new ForeignKeyConstraint(
414 22624
            $localColumnNames,
415 224
            $foreignTable,
416 224
            $foreignColumnNames,
417 224
            $name,
418 224
            $options
419
        );
420 22624
        $this->_addForeignKeyConstraint($constraint);
421
422 22624
        return $this;
423
    }
424
425
    /**
426
     * @param string $name
427
     * @param mixed  $value
428
     *
429
     * @return self
430
     */
431 20322
    public function addOption($name, $value)
432
    {
433 20322
        $this->_options[$name] = $value;
434
435 20322
        return $this;
436
    }
437
438
    /**
439
     * @return void
440
     *
441
     * @throws SchemaException
442
     */
443 24395
    protected function _addColumn(Column $column)
444
    {
445 24395
        $columnName = $column->getName();
446 24395
        $columnName = $this->normalizeIdentifier($columnName);
447
448 24395
        if (isset($this->_columns[$columnName])) {
449 1356
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
450
        }
451
452 24395
        $this->_columns[$columnName] = $column;
453 24395
    }
454
455
    /**
456
     * Adds an index to the table.
457
     *
458
     * @return self
459
     *
460
     * @throws SchemaException
461
     */
462 23875
    protected function _addIndex(Index $indexCandidate)
463
    {
464 23875
        $indexName               = $indexCandidate->getName();
465 23875
        $indexName               = $this->normalizeIdentifier($indexName);
466 23875
        $replacedImplicitIndexes = [];
467
468 23875
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
469 18945
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
470 18937
                continue;
471
            }
472
473 662
            $replacedImplicitIndexes[] = $name;
474
        }
475
476 23875
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
477 23875
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
478
        ) {
479 1233
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
480
        }
481
482 23875
        foreach ($replacedImplicitIndexes as $name) {
483 662
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
484
        }
485
486 23875
        if ($indexCandidate->isPrimary()) {
487 23571
            $this->_primaryKeyName = $indexName;
488
        }
489
490 23875
        $this->_indexes[$indexName] = $indexCandidate;
491
492 23875
        return $this;
493
    }
494
495
    /**
496
     * @return void
497
     */
498 22713
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
499
    {
500 22713
        $constraint->setLocalTable($this);
501
502 22713
        if (strlen($constraint->getName())) {
503 22632
            $name = $constraint->getName();
504
        } else {
505 1953
            $name = $this->_generateIdentifierName(
506 1953
                array_merge((array) $this->getName(), $constraint->getLocalColumns()),
507 1953
                'fk',
508 1953
                $this->_getMaxIdentifierLength()
509
            );
510
        }
511 22713
        $name = $this->normalizeIdentifier($name);
512
513 22713
        $this->_fkConstraints[$name] = $constraint;
514
515
        // add an explicit index on the foreign key columns. If there is already an index that fulfils this requirements drop the request.
516
        // In the case of __construct calling this method during hydration from schema-details all the explicitly added indexes
517
        // lead to duplicates. This creates computation overhead in this case, however no duplicate indexes are ever added (based on columns).
518 22713
        $indexName      = $this->_generateIdentifierName(
519 22713
            array_merge([$this->getName()], $constraint->getColumns()),
520 22713
            'idx',
521 22713
            $this->_getMaxIdentifierLength()
522
        );
523 22713
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
524
525 22713
        foreach ($this->_indexes as $existingIndex) {
526 22661
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
527 21057
                return;
528
            }
529
        }
530
531 22653
        $this->_addIndex($indexCandidate);
532 22653
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
533 22653
    }
534
535
    /**
536
     * Returns whether this table has a foreign key constraint with the given name.
537
     *
538
     * @param string $constraintName
539
     *
540
     * @return bool
541
     */
542 19790
    public function hasForeignKey($constraintName)
543
    {
544 19790
        $constraintName = $this->normalizeIdentifier($constraintName);
545
546 19790
        return isset($this->_fkConstraints[$constraintName]);
547
    }
548
549
    /**
550
     * Returns the foreign key constraint with the given name.
551
     *
552
     * @param string $constraintName The constraint name.
553
     *
554
     * @return ForeignKeyConstraint
555
     *
556
     * @throws SchemaException If the foreign key does not exist.
557
     */
558 272
    public function getForeignKey($constraintName)
559
    {
560 272
        $constraintName = $this->normalizeIdentifier($constraintName);
561 272
        if (! $this->hasForeignKey($constraintName)) {
562
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
563
        }
564
565 272
        return $this->_fkConstraints[$constraintName];
566
    }
567
568
    /**
569
     * Removes the foreign key constraint with the given name.
570
     *
571
     * @param string $constraintName The constraint name.
572
     *
573
     * @return void
574
     *
575
     * @throws SchemaException
576
     */
577 274
    public function removeForeignKey($constraintName)
578
    {
579 274
        $constraintName = $this->normalizeIdentifier($constraintName);
580 274
        if (! $this->hasForeignKey($constraintName)) {
581
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
582
        }
583
584 274
        unset($this->_fkConstraints[$constraintName]);
585 274
    }
586
587
    /**
588
     * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
589
     *
590
     * @return Column[]
591
     */
592 23945
    public function getColumns()
593
    {
594 23945
        $primaryKey        = $this->getPrimaryKey();
595 23945
        $primaryKeyColumns = [];
596
597 23945
        if ($primaryKey !== null) {
598 23331
            $primaryKeyColumns = $this->filterColumns($primaryKey->getColumns());
599
        }
600
601 23945
        return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
602
    }
603
604
    /**
605
     * Returns foreign key columns
606
     *
607
     * @return Column[]
608
     */
609 23945
    private function getForeignKeyColumns()
610
    {
611 23945
        $foreignKeyColumns = [];
612 23945
        foreach ($this->getForeignKeys() as $foreignKey) {
613 22589
            $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
614
        }
615
616 23945
        return $this->filterColumns($foreignKeyColumns);
617
    }
618
619
    /**
620
     * Returns only columns that have specified names
621
     *
622
     * @param string[] $columnNames
623
     *
624
     * @return Column[]
625
     */
626 23945
    private function filterColumns(array $columnNames)
627
    {
628
        return array_filter($this->_columns, static function ($columnName) use ($columnNames) {
629 23893
            return in_array($columnName, $columnNames, true);
630 23945
        }, ARRAY_FILTER_USE_KEY);
631
    }
632
633
    /**
634
     * Returns whether this table has a Column with the given name.
635
     *
636
     * @param string $columnName The column name.
637
     *
638
     * @return bool
639
     */
640 24063
    public function hasColumn($columnName)
641
    {
642 24063
        $columnName = $this->normalizeIdentifier($columnName);
643
644 24063
        return isset($this->_columns[$columnName]);
645
    }
646
647
    /**
648
     * Returns the Column with the given name.
649
     *
650
     * @param string $columnName The column name.
651
     *
652
     * @return Column
653
     *
654
     * @throws SchemaException If the column does not exist.
655
     */
656 23795
    public function getColumn($columnName)
657
    {
658 23795
        $columnName = $this->normalizeIdentifier($columnName);
659 23795
        if (! $this->hasColumn($columnName)) {
660 1381
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
661
        }
662
663 23793
        return $this->_columns[$columnName];
664
    }
665
666
    /**
667
     * Returns the primary key.
668
     *
669
     * @return Index|null The primary key, or null if this Table has no primary key.
670
     */
671 23955
    public function getPrimaryKey()
672
    {
673 23955
        if (! $this->hasPrimaryKey()) {
674 23180
            return null;
675
        }
676
677 23341
        return $this->getIndex($this->_primaryKeyName);
678
    }
679
680
    /**
681
     * Returns the primary key columns.
682
     *
683
     * @return string[]
684
     *
685
     * @throws DBALException
686
     */
687 20143
    public function getPrimaryKeyColumns()
688
    {
689 20143
        $primaryKey = $this->getPrimaryKey();
690
691 20143
        if ($primaryKey === null) {
692
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
693
        }
694
695 20143
        return $primaryKey->getColumns();
696
    }
697
698
    /**
699
     * Returns whether this table has a primary key.
700
     *
701
     * @return bool
702
     */
703 23961
    public function hasPrimaryKey()
704
    {
705 23961
        return $this->_primaryKeyName && $this->hasIndex($this->_primaryKeyName);
706
    }
707
708
    /**
709
     * Returns whether this table has an Index with the given name.
710
     *
711
     * @param string $indexName The index name.
712
     *
713
     * @return bool
714
     */
715 23445
    public function hasIndex($indexName)
716
    {
717 23445
        $indexName = $this->normalizeIdentifier($indexName);
718
719 23445
        return isset($this->_indexes[$indexName]);
720
    }
721
722
    /**
723
     * Returns the Index with the given name.
724
     *
725
     * @param string $indexName The index name.
726
     *
727
     * @return Index
728
     *
729
     * @throws SchemaException If the index does not exist.
730
     */
731 23401
    public function getIndex($indexName)
732
    {
733 23401
        $indexName = $this->normalizeIdentifier($indexName);
734 23401
        if (! $this->hasIndex($indexName)) {
735 1256
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
736
        }
737
738 23399
        return $this->_indexes[$indexName];
739
    }
740
741
    /**
742
     * @return Index[]
743
     */
744 23889
    public function getIndexes()
745
    {
746 23889
        return $this->_indexes;
747
    }
748
749
    /**
750
     * Returns the foreign key constraints.
751
     *
752
     * @return ForeignKeyConstraint[]
753
     */
754 23981
    public function getForeignKeys()
755
    {
756 23981
        return $this->_fkConstraints;
757
    }
758
759
    /**
760
     * @param string $name
761
     *
762
     * @return bool
763
     */
764 20307
    public function hasOption($name)
765
    {
766 20307
        return isset($this->_options[$name]);
767
    }
768
769
    /**
770
     * @param string $name
771
     *
772
     * @return mixed
773
     */
774 19658
    public function getOption($name)
775
    {
776 19658
        return $this->_options[$name];
777
    }
778
779
    /**
780
     * @return mixed[]
781
     */
782 23675
    public function getOptions()
783
    {
784 23675
        return $this->_options;
785
    }
786
787
    /**
788
     * @return void
789
     */
790 22325
    public function visit(Visitor $visitor)
791
    {
792 22325
        $visitor->acceptTable($this);
793
794 22325
        foreach ($this->getColumns() as $column) {
795 22319
            $visitor->acceptColumn($this, $column);
796
        }
797
798 22325
        foreach ($this->getIndexes() as $index) {
799 20164
            $visitor->acceptIndex($this, $index);
800
        }
801
802 22325
        foreach ($this->getForeignKeys() as $constraint) {
803 158
            $visitor->acceptForeignKey($this, $constraint);
804
        }
805 22325
    }
806
807
    /**
808
     * Clone of a Table triggers a deep clone of all affected assets.
809
     *
810
     * @return void
811
     */
812 21597
    public function __clone()
813
    {
814 21597
        foreach ($this->_columns as $k => $column) {
815 21595
            $this->_columns[$k] = clone $column;
816
        }
817 21597
        foreach ($this->_indexes as $k => $index) {
818 21573
            $this->_indexes[$k] = clone $index;
819
        }
820 21597
        foreach ($this->_fkConstraints as $k => $fk) {
821 20464
            $this->_fkConstraints[$k] = clone $fk;
822 20464
            $this->_fkConstraints[$k]->setLocalTable($this);
823
        }
824 21597
    }
825
826
    /**
827
     * Normalizes a given identifier.
828
     *
829
     * Trims quotes and lowercases the given identifier.
830
     *
831
     * @param string|null $identifier The identifier to normalize.
832
     *
833
     * @return string The normalized identifier.
834
     */
835 24403
    private function normalizeIdentifier($identifier)
836
    {
837 24403
        if ($identifier === null) {
838 381
            return '';
839
        }
840
841 24403
        return $this->trimQuotes(strtolower($identifier));
842
    }
843
}
844