Failed Conditions
Pull Request — master (#3512)
by David
17:16
created

Table::getComment()   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
eloc 1
dl 0
loc 3
ccs 2
cts 2
cp 1
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 1
1
<?php
2
3
namespace Doctrine\DBAL\Schema;
4
5
use Doctrine\DBAL\DBALException;
6
use Doctrine\DBAL\Schema\Visitor\Visitor;
7
use Doctrine\DBAL\Types\Type;
8
use 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 24766
    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 24766
        if (strlen($tableName) === 0) {
57 1565
            throw DBALException::invalidTableName($tableName);
58
        }
59
60 24764
        $this->_setName($tableName);
61
62 24764
        foreach ($columns as $column) {
63 22241
            $this->_addColumn($column);
64
        }
65
66 24762
        foreach ($indexes as $idx) {
67 21993
            $this->_addIndex($idx);
68
        }
69
70 24758
        foreach ($fkConstraints as $constraint) {
71 21061
            $this->_addForeignKeyConstraint($constraint);
72
        }
73
74 24758
        $this->_options = array_merge($this->_options, $options);
75 24758
    }
76
77
    /**
78
     * @return void
79
     */
80 22716
    public function setSchemaConfig(SchemaConfig $schemaConfig)
81
    {
82 22716
        $this->_schemaConfig = $schemaConfig;
83 22716
    }
84
85
    /**
86
     * @return int
87
     */
88 22977
    protected function _getMaxIdentifierLength()
89
    {
90 22977
        if ($this->_schemaConfig instanceof SchemaConfig) {
91 22512
            return $this->_schemaConfig->getMaxIdentifierLength();
92
        }
93
94 22912
        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 23684
    public function setPrimaryKey(array $columnNames, $indexName = false)
106
    {
107 23684
        $this->_addIndex($this->_createIndex($columnNames, $indexName ?: 'primary', true, true));
108
109 23684
        foreach ($columnNames as $columnName) {
110 23684
            $column = $this->getColumn($columnName);
111 23684
            $column->setNotnull(true);
112
        }
113
114 23684
        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 21519
    public function addIndex(array $columnNames, $indexName = null, array $flags = [], array $options = [])
126
    {
127 21519
        if ($indexName === null) {
128 20996
            $indexName = $this->_generateIdentifierName(
129 20996
                array_merge([$this->getName()], $columnNames),
130 20996
                'idx',
131 20996
                $this->_getMaxIdentifierLength()
132
            );
133
        }
134
135 21519
        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 20255
    public function dropPrimaryKey()
144
    {
145 20255
        $this->dropIndex($this->_primaryKeyName);
146 20255
        $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 20255
    }
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 20292
    public function dropIndex($indexName)
159
    {
160 20292
        $indexName = $this->normalizeIdentifier($indexName);
161 20292
        if (! $this->hasIndex($indexName)) {
162
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
163
        }
164 20292
        unset($this->_indexes[$indexName]);
165 20292
    }
166
167
    /**
168
     * @param string[]    $columnNames
169
     * @param string|null $indexName
170
     * @param mixed[]     $options
171
     *
172
     * @return self
173
     */
174 22570
    public function addUniqueIndex(array $columnNames, $indexName = null, array $options = [])
175
    {
176 22570
        if ($indexName === null) {
177 22213
            $indexName = $this->_generateIdentifierName(
178 22213
                array_merge([$this->getName()], $columnNames),
179 22213
                'uniq',
180 22213
                $this->_getMaxIdentifierLength()
181
            );
182
        }
183
184 22570
        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 19884
    public function renameIndex($oldIndexName, $newIndexName = null)
200
    {
201 19884
        $oldIndexName           = $this->normalizeIdentifier($oldIndexName);
202 19884
        $normalizedNewIndexName = $this->normalizeIdentifier($newIndexName);
203
204 19884
        if ($oldIndexName === $normalizedNewIndexName) {
205 431
            return $this;
206
        }
207
208 19884
        if (! $this->hasIndex($oldIndexName)) {
209 340
            throw SchemaException::indexDoesNotExist($oldIndexName, $this->_name);
210
        }
211
212 19882
        if ($this->hasIndex($normalizedNewIndexName)) {
213 315
            throw SchemaException::indexAlreadyExists($normalizedNewIndexName, $this->_name);
214
        }
215
216 19880
        $oldIndex = $this->_indexes[$oldIndexName];
217
218 19880
        if ($oldIndex->isPrimary()) {
219 415
            $this->dropPrimaryKey();
220
221 415
            return $this->setPrimaryKey($oldIndex->getColumns(), $newIndexName ?? false);
222
        }
223
224 19880
        unset($this->_indexes[$oldIndexName]);
225
226 19880
        if ($oldIndex->isUnique()) {
227 417
            return $this->addUniqueIndex($oldIndex->getColumns(), $newIndexName, $oldIndex->getOptions());
228
        }
229
230 19878
        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 20059
    public function columnsAreIndexed(array $columnNames)
241
    {
242 20059
        foreach ($this->getIndexes() as $index) {
243
            /** @var $index Index */
244 20059
            if ($index->spansColumns($columnNames)) {
245 20059
                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 23982
    private function _createIndex(array $columnNames, $indexName, $isUnique, $isPrimary, array $flags = [], array $options = [])
265
    {
266 23982
        if (preg_match('(([^a-zA-Z0-9_]+))', $this->normalizeIdentifier($indexName))) {
267 1090
            throw SchemaException::indexNameInvalid($indexName);
268
        }
269
270 23980
        foreach ($columnNames as $columnName) {
271 23978
            if (! $this->hasColumn($columnName)) {
272 2059
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
273
            }
274
        }
275
276 23978
        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 24412
    public function addColumn($columnName, $typeName, array $options = [])
287
    {
288 24412
        $column = new Column($columnName, Type::getType($typeName), $options);
289
290 24412
        $this->_addColumn($column);
291
292 24412
        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 20507
    public function changeColumn($columnName, array $options)
321
    {
322 20507
        $column = $this->getColumn($columnName);
323 20507
        $column->setOptions($options);
324
325 20507
        return $this;
326
    }
327
328
    /**
329
     * Drops a Column from the Table.
330
     *
331
     * @param string $columnName
332
     *
333
     * @return self
334
     */
335 1456
    public function dropColumn($columnName)
336
    {
337 1456
        $columnName = $this->normalizeIdentifier($columnName);
338 1456
        unset($this->_columns[$columnName]);
339
340 1456
        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 22740
    public function addForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [], $constraintName = null)
357
    {
358 22740
        $constraintName = $constraintName ?: $this->_generateIdentifierName(array_merge((array) $this->getName(), $localColumnNames), 'fk', $this->_getMaxIdentifierLength());
359
360 22740
        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 15992
    public function addUnnamedForeignKeyConstraint($foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
378
    {
379 15992
        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 22742
    public function addNamedForeignKeyConstraint($name, $foreignTable, array $localColumnNames, array $foreignColumnNames, array $options = [])
398
    {
399 22742
        if ($foreignTable instanceof Table) {
400 22252
            foreach ($foreignColumnNames as $columnName) {
401 22252
                if (! $foreignTable->hasColumn($columnName)) {
402 1104
                    throw SchemaException::columnDoesNotExist($columnName, $foreignTable->getName());
403
                }
404
            }
405
        }
406
407 22740
        foreach ($localColumnNames as $columnName) {
408 22740
            if (! $this->hasColumn($columnName)) {
409 1239
                throw SchemaException::columnDoesNotExist($columnName, $this->_name);
410
            }
411
        }
412
413 22738
        $constraint = new ForeignKeyConstraint(
414 22738
            $localColumnNames,
415 224
            $foreignTable,
416 224
            $foreignColumnNames,
417 224
            $name,
418 224
            $options
419
        );
420 22738
        $this->_addForeignKeyConstraint($constraint);
421
422 22738
        return $this;
423
    }
424
425
    /**
426
     * @param string $name
427
     * @param mixed  $value
428
     *
429
     * @return self
430
     */
431 22178
    public function addOption($name, $value)
432
    {
433 22178
        $this->_options[$name] = $value;
434
435 22178
        return $this;
436
    }
437
438
    /**
439
     * @return void
440
     *
441
     * @throws SchemaException
442
     */
443 24516
    protected function _addColumn(Column $column)
444
    {
445 24516
        $columnName = $column->getName();
446 24516
        $columnName = $this->normalizeIdentifier($columnName);
447
448 24516
        if (isset($this->_columns[$columnName])) {
449 1390
            throw SchemaException::columnAlreadyExists($this->getName(), $columnName);
450
        }
451
452 24516
        $this->_columns[$columnName] = $column;
453 24516
    }
454
455
    /**
456
     * Adds an index to the table.
457
     *
458
     * @return self
459
     *
460
     * @throws SchemaException
461
     */
462 23992
    protected function _addIndex(Index $indexCandidate)
463
    {
464 23992
        $indexName               = $indexCandidate->getName();
465 23992
        $indexName               = $this->normalizeIdentifier($indexName);
466 23992
        $replacedImplicitIndexes = [];
467
468 23992
        foreach ($this->implicitIndexes as $name => $implicitIndex) {
469 19014
            if (! $implicitIndex->isFullfilledBy($indexCandidate) || ! isset($this->_indexes[$name])) {
470 19006
                continue;
471
            }
472
473 696
            $replacedImplicitIndexes[] = $name;
474
        }
475
476 23992
        if ((isset($this->_indexes[$indexName]) && ! in_array($indexName, $replacedImplicitIndexes, true)) ||
477 23992
            ($this->_primaryKeyName !== false && $indexCandidate->isPrimary())
478
        ) {
479 1267
            throw SchemaException::indexAlreadyExists($indexName, $this->_name);
480
        }
481
482 23992
        foreach ($replacedImplicitIndexes as $name) {
483 696
            unset($this->_indexes[$name], $this->implicitIndexes[$name]);
484
        }
485
486 23992
        if ($indexCandidate->isPrimary()) {
487 23688
            $this->_primaryKeyName = $indexName;
488
        }
489
490 23992
        $this->_indexes[$indexName] = $indexCandidate;
491
492 23992
        return $this;
493
    }
494
495
    /**
496
     * @return void
497
     */
498 22824
    protected function _addForeignKeyConstraint(ForeignKeyConstraint $constraint)
499
    {
500 22824
        $constraint->setLocalTable($this);
501
502 22824
        if (strlen($constraint->getName())) {
503 22746
            $name = $constraint->getName();
504
        } else {
505 1985
            $name = $this->_generateIdentifierName(
506 1985
                array_merge((array) $this->getName(), $constraint->getLocalColumns()),
507 1985
                'fk',
508 1985
                $this->_getMaxIdentifierLength()
509
            );
510
        }
511 22824
        $name = $this->normalizeIdentifier($name);
512
513 22824
        $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 22824
        $indexName      = $this->_generateIdentifierName(
519 22824
            array_merge([$this->getName()], $constraint->getColumns()),
520 22824
            'idx',
521 22824
            $this->_getMaxIdentifierLength()
522
        );
523 22824
        $indexCandidate = $this->_createIndex($constraint->getColumns(), $indexName, false, false);
524
525 22824
        foreach ($this->_indexes as $existingIndex) {
526 22772
            if ($indexCandidate->isFullfilledBy($existingIndex)) {
527 21143
                return;
528
            }
529
        }
530
531 22764
        $this->_addIndex($indexCandidate);
532 22764
        $this->implicitIndexes[$this->normalizeIdentifier($indexName)] = $indexCandidate;
533 22764
    }
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 19875
    public function hasForeignKey($constraintName)
543
    {
544 19875
        $constraintName = $this->normalizeIdentifier($constraintName);
545
546 19875
        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 306
    public function getForeignKey($constraintName)
559
    {
560 306
        $constraintName = $this->normalizeIdentifier($constraintName);
561 306
        if (! $this->hasForeignKey($constraintName)) {
562
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
563
        }
564
565 306
        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 308
    public function removeForeignKey($constraintName)
578
    {
579 308
        $constraintName = $this->normalizeIdentifier($constraintName);
580 308
        if (! $this->hasForeignKey($constraintName)) {
581
            throw SchemaException::foreignKeyDoesNotExist($constraintName, $this->_name);
582
        }
583
584 308
        unset($this->_fkConstraints[$constraintName]);
585 308
    }
586
587
    /**
588
     * Returns ordered list of columns (primary keys are first, then foreign keys, then the rest)
589
     *
590
     * @return Column[]
591
     */
592 24066
    public function getColumns()
593
    {
594 24066
        $primaryKey        = $this->getPrimaryKey();
595 24066
        $primaryKeyColumns = [];
596
597 24066
        if ($primaryKey !== null) {
598 23448
            $primaryKeyColumns = $this->filterColumns($primaryKey->getColumns());
599
        }
600
601 24066
        return array_merge($primaryKeyColumns, $this->getForeignKeyColumns(), $this->_columns);
602
    }
603
604
    /**
605
     * Returns foreign key columns
606
     *
607
     * @return Column[]
608
     */
609 24066
    private function getForeignKeyColumns()
610
    {
611 24066
        $foreignKeyColumns = [];
612 24066
        foreach ($this->getForeignKeys() as $foreignKey) {
613 22700
            $foreignKeyColumns = array_merge($foreignKeyColumns, $foreignKey->getColumns());
614
        }
615
616 24066
        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 24066
    private function filterColumns(array $columnNames)
627
    {
628
        return array_filter($this->_columns, static function ($columnName) use ($columnNames) {
629 24014
            return in_array($columnName, $columnNames, true);
630 24066
        }, 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 24180
    public function hasColumn($columnName)
641
    {
642 24180
        $columnName = $this->normalizeIdentifier($columnName);
643
644 24180
        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 23912
    public function getColumn($columnName)
657
    {
658 23912
        $columnName = $this->normalizeIdentifier($columnName);
659 23912
        if (! $this->hasColumn($columnName)) {
660 1415
            throw SchemaException::columnDoesNotExist($columnName, $this->_name);
661
        }
662
663 23910
        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 24076
    public function getPrimaryKey()
672
    {
673 24076
        if (! $this->hasPrimaryKey()) {
674 23300
            return null;
675
        }
676
677 23458
        return $this->getIndex($this->_primaryKeyName);
678
    }
679
680
    /**
681
     * Returns the primary key columns.
682
     *
683
     * @return string[]
684
     *
685
     * @throws DBALException
686
     */
687 20245
    public function getPrimaryKeyColumns()
688
    {
689 20245
        $primaryKey = $this->getPrimaryKey();
690
691 20245
        if ($primaryKey === null) {
692
            throw new DBALException('Table ' . $this->getName() . ' has no primary key.');
693
        }
694
695 20245
        return $primaryKey->getColumns();
696
    }
697
698
    /**
699
     * Returns whether this table has a primary key.
700
     *
701
     * @return bool
702
     */
703 24082
    public function hasPrimaryKey()
704
    {
705 24082
        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 23562
    public function hasIndex($indexName)
716
    {
717 23562
        $indexName = $this->normalizeIdentifier($indexName);
718
719 23562
        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 23518
    public function getIndex($indexName)
732
    {
733 23518
        $indexName = $this->normalizeIdentifier($indexName);
734 23518
        if (! $this->hasIndex($indexName)) {
735 1290
            throw SchemaException::indexDoesNotExist($indexName, $this->_name);
736
        }
737
738 23516
        return $this->_indexes[$indexName];
739
    }
740
741
    /**
742
     * @return Index[]
743
     */
744 24010
    public function getIndexes()
745
    {
746 24010
        return $this->_indexes;
747
    }
748
749
    /**
750
     * Returns the foreign key constraints.
751
     *
752
     * @return ForeignKeyConstraint[]
753
     */
754 24102
    public function getForeignKeys()
755
    {
756 24102
        return $this->_fkConstraints;
757
    }
758
759
    /**
760
     * @param string $name
761
     *
762
     * @return bool
763
     */
764 22788
    public function hasOption($name)
765
    {
766 22788
        return isset($this->_options[$name]);
767
    }
768
769
    /**
770
     * @param string $name
771
     *
772
     * @return mixed
773
     */
774 20611
    public function getOption($name)
775
    {
776 20611
        return $this->_options[$name];
777
    }
778
779
    /**
780
     * @return mixed[]
781
     */
782 23796
    public function getOptions()
783
    {
784 23796
        return $this->_options;
785
    }
786
787
    /**
788
     * @return void
789
     */
790 22433
    public function visit(Visitor $visitor)
791
    {
792 22433
        $visitor->acceptTable($this);
793
794 22433
        foreach ($this->getColumns() as $column) {
795 22427
            $visitor->acceptColumn($this, $column);
796
        }
797
798 22433
        foreach ($this->getIndexes() as $index) {
799 20270
            $visitor->acceptIndex($this, $index);
800
        }
801
802 22433
        foreach ($this->getForeignKeys() as $constraint) {
803 158
            $visitor->acceptForeignKey($this, $constraint);
804
        }
805 22433
    }
806
807
    /**
808
     * Clone of a Table triggers a deep clone of all affected assets.
809
     *
810
     * @return void
811
     */
812 21705
    public function __clone()
813
    {
814 21705
        foreach ($this->_columns as $k => $column) {
815 21703
            $this->_columns[$k] = clone $column;
816
        }
817 21705
        foreach ($this->_indexes as $k => $index) {
818 21681
            $this->_indexes[$k] = clone $index;
819
        }
820 21705
        foreach ($this->_fkConstraints as $k => $fk) {
821 20550
            $this->_fkConstraints[$k] = clone $fk;
822 20550
            $this->_fkConstraints[$k]->setLocalTable($this);
823
        }
824 21705
    }
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 24524
    private function normalizeIdentifier($identifier)
836
    {
837 24524
        if ($identifier === null) {
838 415
            return '';
839
        }
840
841 24524
        return $this->trimQuotes(strtolower($identifier));
842
    }
843
844 20037
    public function setComment(?string $comment) : self
845
    {
846
        // For keeping backward compatibility with MySQL in previous releases, table comments are stored as options.
847 20037
        $this->addOption('comment', $comment);
848
849 20037
        return $this;
850
    }
851
852 20037
    public function getComment() : ?string
853
    {
854 20037
        return $this->_options['comment'] ?? null;
855
    }
856
}
857