Completed
Pull Request — master (#1904)
by
unknown
02:25
created

PdoAdapter::getAddColumnInstructions()

Size

Total Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1
c 0
b 0
f 0
ccs 0
cts 0
cp 0
nc 1
1
<?php
2
3
/**
4
 * MIT License
5
 * For full license information, please view the LICENSE file that was distributed with this source code.
6
 */
7
8
namespace Phinx\Db\Adapter;
9
10
use BadMethodCallException;
11
use InvalidArgumentException;
12
use PDO;
13
use PDOException;
14
use Phinx\Config\Config;
15
use Phinx\Db\Action\AddColumn;
16
use Phinx\Db\Action\AddForeignKey;
17
use Phinx\Db\Action\AddIndex;
18
use Phinx\Db\Action\ChangeColumn;
19
use Phinx\Db\Action\ChangeComment;
20
use Phinx\Db\Action\ChangePrimaryKey;
21
use Phinx\Db\Action\DropForeignKey;
22
use Phinx\Db\Action\DropIndex;
23
use Phinx\Db\Action\DropTable;
24
use Phinx\Db\Action\RemoveColumn;
25
use Phinx\Db\Action\RenameColumn;
26
use Phinx\Db\Action\RenameTable;
27
use Phinx\Db\Table as DbTable;
28
use Phinx\Db\Table\Column;
29
use Phinx\Db\Table\ForeignKey;
30
use Phinx\Db\Table\Index;
31
use Phinx\Db\Table\Table;
32
use Phinx\Db\Util\AlterInstructions;
33
use Phinx\Migration\MigrationInterface;
34
use RuntimeException;
35
use Symfony\Component\Console\Output\OutputInterface;
36
37
/**
38
 * Phinx PDO Adapter.
39
 *
40
 * @author Rob Morgan <[email protected]>
41
 */
42
abstract class PdoAdapter extends AbstractAdapter implements DirectActionInterface
43
{
44
    /**
45
     * @var \PDO|null
46
     */
47
    protected $connection;
48
49
    /**
50
     * Writes a message to stdout if verbose output is on
51 287
     *
52
     * @param string $message The message to show
53 287
     *
54
     * @return void
55 287
     */
56 3
    protected function verboseLog($message)
57 3
    {
58
        if (
59 287
            !$this->isDryRunEnabled() &&
60
             $this->getOutput()->getVerbosity() < OutputInterface::VERBOSITY_VERY_VERBOSE
61
        ) {
62
            return;
63
        }
64
65
        $this->getOutput()->writeln($message);
66
    }
67
68 193
    /**
69
     * Create PDO connection
70 193
     *
71
     * @param string $dsn Connection string
72
     * @param string|null $username Database username
73 193
     * @param string|null $password Database password
74 191
     * @param array $options Connection options
75 191
     * @return \PDO
76 74
     */
77 74
    protected function createPdoConnection($dsn, $username = null, $password = null, array $options = [])
78
    {
79
        $adapterOptions = $this->getOptions();
80
81
        if (!isset($adapterOptions['attr_errmode'])) {
82
            $adapterOptions['attr_errmode'] = PDO::ERRMODE_EXCEPTION;
83
        }
84
85
        try {
86 74
            $db = new PDO($dsn, $username, $password, $options);
87
88 View Code Duplication
            foreach ($adapterOptions as $key => $option) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
89
                if (strpos($key, 'attr_') === 0) {
90
                    $pdoConstant = '\PDO::' . strtoupper($key);
91
                    if (!defined($pdoConstant)) {
92
                        throw new \UnexpectedValueException('Invalid PDO attribute: ' . $key . '(' . $pdoConstant . ')');
93 193
                    }
94
                    $db->setAttribute(constant($pdoConstant), $option);
95
                }
96
            }
97
        } catch (PDOException $e) {
98
            throw new InvalidArgumentException(sprintf(
99
                'There was a problem connecting to the database: %s',
100
                $e->getMessage()
101 191
            ), $e->getCode(), $e);
102
        }
103 191
104 189
        return $db;
105 189
    }
106 191
107
    /**
108
     * @inheritDoc
109
     */
110
    public function setOptions(array $options)
111
    {
112 1
        parent::setOptions($options);
113
114 1
        if (isset($options['connection'])) {
115
            $this->setConnection($options['connection']);
116
        }
117
118
        return $this;
119
    }
120
121
    /**
122
     * Sets the database connection.
123
     *
124
     * @param \PDO $connection Connection
125
     *
126 218
     * @return \Phinx\Db\Adapter\AdapterInterface
127
     */
128 218
    public function setConnection(PDO $connection)
129 3
    {
130 3
        $this->connection = $connection;
131
132
        // Create the schema table if it doesn't already exist
133 217
        if (!$this->hasSchemaTable()) {
134
            $this->createSchemaTable();
135
        } else {
136
            $table = new DbTable($this->getSchemaTableName(), [], $this);
137
            if (!$table->hasColumn('migration_name')) {
138
                $table
139
                    ->addColumn(
140
                        'migration_name',
141
                        'string',
142 220
                        ['limit' => 100, 'after' => 'version', 'default' => null, 'null' => true]
143
                    )
144 220
                    ->save();
145
            }
146
            if (!$table->hasColumn('breakpoint')) {
147
                $table
148
                    ->addColumn('breakpoint', 'boolean', ['default' => false])
149
                    ->save();
150 151
            }
151
        }
152 151
153 151
        return $this;
154
    }
155
156
    /**
157
     * Gets the database connection
158
     *
159 213
     * @return \PDO
160
     */
161 213
    public function getConnection()
162 213
    {
163 213
        if ($this->connection === null) {
164 208
            $this->connect();
165 208
        }
166 213
167
        return $this->connection;
168
    }
169
170
    /**
171
     * @inheritDoc
172 1
     */
173
    public function connect()
174 1
    {
175 1
    }
176 1
177 1
    /**
178
     * @inheritDoc
179 1
     */
180 1
    public function disconnect()
181 1
    {
182
    }
183 1
184 1
    /**
185 1
     * @inheritDoc
186
     */
187
    public function execute($sql)
188
    {
189
        $sql = rtrim($sql, "; \t\n\r\0\x0B") . ';';
190 11
        $this->verboseLog($sql);
191
192 11
        if ($this->isDryRunEnabled()) {
193 11
            return 0;
194 11
        }
195 11
196
        return $this->getConnection()->exec($sql);
197 11
    }
198 11
199 11
    /**
200
     * Returns the Cake\Database connection object using the same underlying
201 11
     * PDO object as this connection.
202 11
     *
203 11
     * @return \Cake\Database\Connection
204 11
     */
205 11
    abstract public function getDecoratedConnection();
206 11
207
    /**
208 11
     * @inheritDoc
209 11
     */
210
    public function getQueryBuilder()
211 11
    {
212 11
        return $this->getDecoratedConnection()->newQuery();
213 11
    }
214
215 11
    /**
216 11
     * Executes a query and returns PDOStatement.
217 11
     *
218
     * @param string $sql SQL
219
     *
220
     * @return \PDOStatement
221
     */
222 5
    public function query($sql)
223
    {
224 5
        return $this->getConnection()->query($sql);
225
    }
226 5
227
    /**
228
     * @inheritDoc
229
     */
230
    public function fetchRow($sql)
231
    {
232 8
        return $this->query($sql)->fetch();
233
    }
234 8
235
    /**
236 8
     * @inheritDoc
237 8
     */
238 6
    public function fetchAll($sql)
239 6
    {
240 2
        return $this->query($sql)->fetchAll();
241 1
    }
242 1
243 1
    /**
244 1
     * @inheritDoc
245 8
     */
246
    public function insert(Table $table, $row)
247 7
    {
248 7
        $sql = sprintf(
249 7
            'INSERT INTO %s ',
250 7
            $this->quoteTableName($table->getName())
251
        );
252 7
        $columns = array_keys($row);
253
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ')';
254
255
        foreach ($row as $column => $value) {
256
            if (is_bool($value)) {
257
                $row[$column] = $this->castToBool($value);
258 5
            }
259
        }
260 5
261
        if ($this->isDryRunEnabled()) {
262 5
            $sql .= ' VALUES (' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ');';
263 5
            $this->output->writeln($sql);
264 5
        } else {
265 5
            $sql .= ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
266 5
            $stmt = $this->getConnection()->prepare($sql);
267 5
            $stmt->execute(array_values($row));
268 5
        }
269 5
    }
270 5
271 5
    /**
272 5
     * Quotes a database value.
273 5
     *
274 5
     * @param mixed $value The value to quote
275 5
     *
276
     * @return mixed
277 5
     */
278 5
    protected function quoteValue($value)
279
    {
280 3
        if (is_numeric($value)) {
281 3
            return $value;
282 3
        }
283 3
284 3
        if ($value === null) {
285 3
            return 'null';
286
        }
287 3
288
        return $this->getConnection()->quote($value);
289
    }
290 5
291
    /**
292
     * Quotes a database string.
293
     *
294
     * @param string $value The string to quote
295
     *
296 1
     * @return string
297
     */
298 1
    protected function quoteString($value)
299 1
    {
300 1
        return $this->getConnection()->quote($value);
301 1
    }
302 1
303 1
    /**
304 1
     * @inheritDoc
305 1
     */
306 1
    public function bulkinsert(Table $table, $rows)
307 1
    {
308 1
        $sql = sprintf(
309 1
            'INSERT INTO %s ',
310
            $this->quoteTableName($table->getName())
311 1
        );
312
        $current = current($rows);
313
        $keys = array_keys($current);
314
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ') VALUES ';
315
316
        if ($this->isDryRunEnabled()) {
317 1
            $values = array_map(function ($row) {
318
                return '(' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ')';
319 1
            }, $rows);
320 1
            $sql .= implode(', ', $values) . ';';
321 1
            $this->output->writeln($sql);
322 1
        } else {
323 1
            $count_keys = count($keys);
324 1
            $query = '(' . implode(', ', array_fill(0, $count_keys, '?')) . ')';
325 1
            $count_vars = count($rows);
326 1
            $queries = array_fill(0, $count_vars, $query);
327 1
            $sql .= implode(',', $queries);
328
            $stmt = $this->getConnection()->prepare($sql);
329
            $vals = [];
330
331
            foreach ($rows as $row) {
332
                foreach ($row as $v) {
333
                    if (is_bool($v)) {
334
                        $vals[] = $this->castToBool($v);
335
                    } else {
336
                        $vals[] = $v;
337
                    }
338
                }
339
            }
340
341
            $stmt->execute($vals);
342
        }
343
    }
344
345
    /**
346
     * @inheritDoc
347
     */
348
    public function getVersions()
349 208
    {
350
        $rows = $this->getVersionLog();
351
352 208
        return array_keys($rows);
353 208
    }
354 208
355 208
    /**
356 208
     * {@inheritDoc}
357 208
     *
358 208
     * @throws \RuntimeException
359 208
     */
360 208
    public function getVersionLog()
361 208
    {
362 208
        $result = [];
363 208
364 208
        switch ($this->options['version_order']) {
365 208
            case Config::VERSION_ORDER_CREATION_TIME:
366 208
                $orderBy = 'version ASC';
367 208
                break;
368
            case Config::VERSION_ORDER_EXECUTION_TIME:
369 208
                $orderBy = 'start_time ASC, version ASC';
370 208
                break;
371 208
            default:
372 208
                throw new RuntimeException('Invalid version_order configuration option');
373 208
        }
374
375
        // This will throw an exception if doing a --dry-run without any migrations as phinxlog
376
        // does not exist, so in that case, we can just expect to trivially return empty set
377
        try {
378
            $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->quoteTableName($this->getSchemaTableName()), $orderBy));
379 121
        } catch (PDOException $e) {
380
            if (!$this->isDryRunEnabled()) {
381 121
                throw $e;
382
            }
383
            $rows = [];
384
        }
385
386
        foreach ($rows as $version) {
387
            $result[$version['version']] = $version;
388
        }
389
390
        return $result;
391
    }
392
393
    /**
394
     * @inheritDoc
395
     */
396
    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
397
    {
398
        if (strcasecmp($direction, MigrationInterface::UP) === 0) {
399
            // up
400
            $sql = sprintf(
401
                "INSERT INTO %s (%s, %s, %s, %s, %s) VALUES ('%s', '%s', '%s', '%s', %s);",
402
                $this->quoteTableName($this->getSchemaTableName()),
403
                $this->quoteColumnName('version'),
404
                $this->quoteColumnName('migration_name'),
405
                $this->quoteColumnName('start_time'),
406
                $this->quoteColumnName('end_time'),
407
                $this->quoteColumnName('breakpoint'),
408
                $migration->getVersion(),
409
                substr($migration->getName(), 0, 100),
410
                $startTime,
411
                $endTime,
412
                $this->castToBool(false)
413
            );
414
415
            $this->execute($sql);
416
        } else {
417
            // down
418
            $sql = sprintf(
419
                "DELETE FROM %s WHERE %s = '%s'",
420
                $this->quoteTableName($this->getSchemaTableName()),
421
                $this->quoteColumnName('version'),
422
                $migration->getVersion()
423
            );
424
425
            $this->execute($sql);
426
        }
427
428
        return $this;
429
    }
430
431
    /**
432
     * @inheritDoc
433
     */
434
    public function toggleBreakpoint(MigrationInterface $migration)
435
    {
436
        $this->query(
437
            sprintf(
438
                'UPDATE %1$s SET %2$s = CASE %2$s WHEN %3$s THEN %4$s ELSE %3$s END, %7$s = %7$s WHERE %5$s = \'%6$s\';',
439
                $this->quoteTableName($this->getSchemaTableName()),
440
                $this->quoteColumnName('breakpoint'),
441
                $this->castToBool(true),
442
                $this->castToBool(false),
443
                $this->quoteColumnName('version'),
444
                $migration->getVersion(),
445
                $this->quoteColumnName('start_time')
446
            )
447
        );
448
449
        return $this;
450
    }
451
452
    /**
453
     * @inheritDoc
454
     */
455
    public function resetAllBreakpoints()
456
    {
457
        return $this->execute(
458
            sprintf(
459
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %2$s <> %3$s;',
460
                $this->quoteTableName($this->getSchemaTableName()),
461
                $this->quoteColumnName('breakpoint'),
462
                $this->castToBool(false),
463
                $this->quoteColumnName('start_time')
464
            )
465
        );
466
    }
467
468
    /**
469
     * @inheritDoc
470
     */
471
    public function setBreakpoint(MigrationInterface $migration)
472
    {
473
        return $this->markBreakpoint($migration, true);
474
    }
475
476
    /**
477
     * @inheritDoc
478
     */
479
    public function unsetBreakpoint(MigrationInterface $migration)
480
    {
481
        return $this->markBreakpoint($migration, false);
482
    }
483
484
    /**
485
     * Mark a migration breakpoint.
486
     *
487
     * @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint
488
     * @param bool $state The required state of the breakpoint
489
     *
490
     * @return \Phinx\Db\Adapter\AdapterInterface
491
     */
492
    protected function markBreakpoint(MigrationInterface $migration, $state)
493
    {
494
        $this->query(
495
            sprintf(
496
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %5$s = \'%6$s\';',
497
                $this->quoteTableName($this->getSchemaTableName()),
498
                $this->quoteColumnName('breakpoint'),
499
                $this->castToBool($state),
500
                $this->quoteColumnName('start_time'),
501
                $this->quoteColumnName('version'),
502
                $migration->getVersion()
503
            )
504
        );
505
506
        return $this;
507
    }
508
509
    /**
510
     * {@inheritDoc}
511
     *
512
     * @throws \BadMethodCallException
513
     *
514
     * @return void
515
     */
516
    public function createSchema($schemaName = 'public')
517
    {
518
        throw new BadMethodCallException('Creating a schema is not supported');
519
    }
520
521
    /**
522
     * {@inheritDoc}
523
     *
524
     * @throws \BadMethodCallException
525
     *
526
     * @return void
527
     */
528
    public function dropSchema($name)
529
    {
530
        throw new BadMethodCallException('Dropping a schema is not supported');
531
    }
532
533
    /**
534
     * @inheritDoc
535
     */
536
    public function getColumnTypes()
537
    {
538
        return [
539
            'string',
540
            'char',
541
            'text',
542
            'tinyinteger',
543
            'smallinteger',
544
            'integer',
545
            'biginteger',
546
            'bit',
547
            'float',
548
            'decimal',
549
            'double',
550
            'datetime',
551
            'timestamp',
552
            'time',
553
            'date',
554
            'blob',
555
            'binary',
556
            'varbinary',
557
            'boolean',
558
            'uuid',
559
            // Geospatial data types
560
            'geometry',
561
            'point',
562
            'linestring',
563
            'polygon',
564
        ];
565
    }
566
567
    /**
568
     * @inheritDoc
569
     */
570
    public function castToBool($value)
571
    {
572
        return (bool)$value ? 1 : 0;
573
    }
574
575
    /**
576
     * Retrieve a database connection attribute
577
     *
578
     * @see http://php.net/manual/en/pdo.getattribute.php
579
     *
580
     * @param int $attribute One of the PDO::ATTR_* constants
581
     *
582
     * @return mixed
583
     */
584
    public function getAttribute($attribute)
585
    {
586
        return $this->connection->getAttribute($attribute);
587
    }
588
589
    /**
590
     * Get the definition for a `DEFAULT` statement.
591
     *
592
     * @param mixed $default Default value
593
     * @param string|null $columnType column type added
594
     *
595
     * @return string
596
     */
597
    protected function getDefaultValueDefinition($default, $columnType = null)
598
    {
599
        // Ensure a defaults of CURRENT_TIMESTAMP(3) is not quoted.
600
        if (is_string($default) && strpos($default, 'CURRENT_TIMESTAMP') !== 0) {
601
            $default = $this->getConnection()->quote($default);
602
        } elseif (is_bool($default)) {
603
            $default = $this->castToBool($default);
604
        } elseif ($default !== null && $columnType === static::PHINX_TYPE_BOOLEAN) {
605
            $default = $this->castToBool((bool)$default);
606
        }
607
608
        return isset($default) ? " DEFAULT $default" : '';
609
    }
610
611
    /**
612
     * Executes all the ALTER TABLE instructions passed for the given table
613
     *
614
     * @param string $tableName The table name to use in the ALTER statement
615
     * @param \Phinx\Db\Util\AlterInstructions $instructions The object containing the alter sequence
616
     *
617
     * @return void
618
     */
619
    protected function executeAlterSteps($tableName, AlterInstructions $instructions)
620
    {
621
        $alter = sprintf('ALTER TABLE %s %%s', $this->quoteTableName($tableName));
622
        $instructions->execute($alter, [$this, 'execute']);
623
    }
624
625
    /**
626
     * @inheritDoc
627
     */
628
    public function addColumn(Table $table, Column $column)
629
    {
630
        $instructions = $this->getAddColumnInstructions($table, $column);
631
        $this->executeAlterSteps($table->getName(), $instructions);
632
    }
633
634
    /**
635
     * Returns the instructions to add the specified column to a database table.
636
     *
637
     * @param \Phinx\Db\Table\Table $table Table
638
     * @param \Phinx\Db\Table\Column $column Column
639
     *
640
     * @return \Phinx\Db\Util\AlterInstructions
641
     */
642
    abstract protected function getAddColumnInstructions(Table $table, Column $column);
643
644
    /**
645
     * @inheritdoc
646
     */
647
    public function renameColumn($tableName, $columnName, $newColumnName)
648
    {
649
        $instructions = $this->getRenameColumnInstructions($tableName, $columnName, $newColumnName);
650
        $this->executeAlterSteps($tableName, $instructions);
651
    }
652
653
    /**
654
     * Returns the instructions to rename the specified column.
655
     *
656
     * @param string $tableName Table name
657
     * @param string $columnName Column Name
658
     * @param string $newColumnName New Column Name
659
     *
660
     * @return \Phinx\Db\Util\AlterInstructions
661
     */
662
    abstract protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName);
663
664
    /**
665
     * @inheritdoc
666
     */
667
    public function changeColumn($tableName, $columnName, Column $newColumn)
668
    {
669
        $instructions = $this->getChangeColumnInstructions($tableName, $columnName, $newColumn);
670
        $this->executeAlterSteps($tableName, $instructions);
671
    }
672
673
    /**
674
     * Returns the instructions to change a table column type.
675
     *
676
     * @param string $tableName Table name
677
     * @param string $columnName Column Name
678
     * @param \Phinx\Db\Table\Column $newColumn New Column
679
     *
680
     * @return \Phinx\Db\Util\AlterInstructions
681
     */
682
    abstract protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn);
683
684
    /**
685
     * @inheritdoc
686
     */
687
    public function dropColumn($tableName, $columnName)
688
    {
689
        $instructions = $this->getDropColumnInstructions($tableName, $columnName);
690
        $this->executeAlterSteps($tableName, $instructions);
691
    }
692
693
    /**
694
     * Returns the instructions to drop the specified column.
695
     *
696
     * @param string $tableName Table name
697
     * @param string $columnName Column Name
698
     *
699
     * @return \Phinx\Db\Util\AlterInstructions
700
     */
701
    abstract protected function getDropColumnInstructions($tableName, $columnName);
702
703
    /**
704
     * @inheritdoc
705
     */
706
    public function addIndex(Table $table, Index $index)
707
    {
708
        $instructions = $this->getAddIndexInstructions($table, $index);
709
        $this->executeAlterSteps($table->getName(), $instructions);
710
    }
711
712
    /**
713
     * Returns the instructions to add the specified index to a database table.
714
     *
715
     * @param \Phinx\Db\Table\Table $table Table
716
     * @param \Phinx\Db\Table\Index $index Index
717
     *
718
     * @return \Phinx\Db\Util\AlterInstructions
719
     */
720
    abstract protected function getAddIndexInstructions(Table $table, Index $index);
721
722
    /**
723
     * @inheritdoc
724
     */
725
    public function dropIndex($tableName, $columns)
726
    {
727
        $instructions = $this->getDropIndexByColumnsInstructions($tableName, $columns);
728
        $this->executeAlterSteps($tableName, $instructions);
729
    }
730
731
    /**
732
     * Returns the instructions to drop the specified index from a database table.
733
     *
734
     * @param string $tableName The name of of the table where the index is
735
     * @param mixed $columns Column(s)
736
     *
737
     * @return \Phinx\Db\Util\AlterInstructions
738
     */
739
    abstract protected function getDropIndexByColumnsInstructions($tableName, $columns);
740
741
    /**
742
     * @inheritdoc
743
     */
744
    public function dropIndexByName($tableName, $indexName)
745
    {
746
        $instructions = $this->getDropIndexByNameInstructions($tableName, $indexName);
747
        $this->executeAlterSteps($tableName, $instructions);
748
    }
749
750
    /**
751
     * Returns the instructions to drop the index specified by name from a database table.
752
     *
753
     * @param string $tableName The table name whe the index is
754
     * @param string $indexName The name of the index
755
     *
756
     * @return \Phinx\Db\Util\AlterInstructions
757
     */
758
    abstract protected function getDropIndexByNameInstructions($tableName, $indexName);
759
760
    /**
761
     * @inheritdoc
762
     */
763
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
764
    {
765
        $instructions = $this->getAddForeignKeyInstructions($table, $foreignKey);
766
        $this->executeAlterSteps($table->getName(), $instructions);
767
    }
768
769
    /**
770
     * Returns the instructions to adds the specified foreign key to a database table.
771
     *
772
     * @param \Phinx\Db\Table\Table $table The table to add the constraint to
773
     * @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add
774
     * @return \Phinx\Db\Util\AlterInstructions
775
     */
776
    abstract protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey);
777
778
    /**
779
     * @inheritDoc
780
     */
781
    public function dropForeignKey($tableName, $columns, $constraint = null)
782
    {
783
        if ($constraint) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $constraint of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
784
            $instructions = $this->getDropForeignKeyInstructions($tableName, $constraint);
785
        } else {
786
            $instructions = $this->getDropForeignKeyByColumnsInstructions($tableName, $columns);
787
        }
788
789
        $this->executeAlterSteps($tableName, $instructions);
790
    }
791
792
    /**
793
     * Returns the instructions to drop the specified foreign key from a database table.
794
     *
795
     * @param string $tableName The table where the foreign key constraint is
796
     * @param string $constraint Constraint name
797
     *
798
     * @return \Phinx\Db\Util\AlterInstructions
799
     */
800
    abstract protected function getDropForeignKeyInstructions($tableName, $constraint);
801
802
    /**
803
     * Returns the instructions to drop the specified foreign key from a database table.
804
     *
805
     * @param string $tableName The table where the foreign key constraint is
806
     * @param string[] $columns The list of column names
807
     *
808
     * @return \Phinx\Db\Util\AlterInstructions
809
     */
810
    abstract protected function getDropForeignKeyByColumnsInstructions($tableName, $columns);
811
812
    /**
813
     * @inheritdoc
814
     */
815
    public function dropTable($tableName)
816
    {
817
        $instructions = $this->getDropTableInstructions($tableName);
818
        $this->executeAlterSteps($tableName, $instructions);
819
    }
820
821
    /**
822
     * Returns the instructions to drop the specified database table.
823
     *
824
     * @param string $tableName Table name
825
     *
826
     * @return \Phinx\Db\Util\AlterInstructions
827
     */
828
    abstract protected function getDropTableInstructions($tableName);
829
830
    /**
831
     * @inheritdoc
832
     */
833
    public function renameTable($tableName, $newTableName)
834
    {
835
        $instructions = $this->getRenameTableInstructions($tableName, $newTableName);
836
        $this->executeAlterSteps($tableName, $instructions);
837
    }
838
839
    /**
840
     * Returns the instructions to rename the specified database table.
841
     *
842
     * @param string $tableName Table name
843
     * @param string $newTableName New Name
844
     *
845
     * @return \Phinx\Db\Util\AlterInstructions
846
     */
847
    abstract protected function getRenameTableInstructions($tableName, $newTableName);
848
849
    /**
850
     * @inheritdoc
851
     */
852
    public function changePrimaryKey(Table $table, $newColumns)
853
    {
854
        $instructions = $this->getChangePrimaryKeyInstructions($table, $newColumns);
855
        $this->executeAlterSteps($table->getName(), $instructions);
856
    }
857
858
    /**
859
     * Returns the instructions to change the primary key for the specified database table.
860
     *
861
     * @param \Phinx\Db\Table\Table $table Table
862
     * @param string|string[]|null $newColumns Column name(s) to belong to the primary key, or null to drop the key
863
     *
864
     * @return \Phinx\Db\Util\AlterInstructions
865
     */
866
    abstract protected function getChangePrimaryKeyInstructions(Table $table, $newColumns);
867
868
    /**
869
     * @inheritdoc
870
     */
871
    public function changeComment(Table $table, $newComment)
872
    {
873
        $instructions = $this->getChangeCommentInstructions($table, $newComment);
874
        $this->executeAlterSteps($table->getName(), $instructions);
875
    }
876
877
    /**
878
     * Returns the instruction to change the comment for the specified database table.
879
     *
880
     * @param \Phinx\Db\Table\Table $table Table
881
     * @param string|null $newComment New comment string, or null to drop the comment
882
     *
883
     * @return \Phinx\Db\Util\AlterInstructions
884
     */
885
    abstract protected function getChangeCommentInstructions(Table $table, $newComment);
886
887
    /**
888
     * {@inheritDoc}
889
     *
890
     * @throws \InvalidArgumentException
891
     *
892
     * @return void
893
     */
894
    public function executeActions(Table $table, array $actions)
895
    {
896
        $instructions = new AlterInstructions();
897
898
        foreach ($actions as $action) {
899
            switch (true) {
900
                case ($action instanceof AddColumn):
901
                    $instructions->merge($this->getAddColumnInstructions($table, $action->getColumn()));
902
                    break;
903
904
                case ($action instanceof AddIndex):
905
                    $instructions->merge($this->getAddIndexInstructions($table, $action->getIndex()));
906
                    break;
907
908
                case ($action instanceof AddForeignKey):
909
                    $instructions->merge($this->getAddForeignKeyInstructions($table, $action->getForeignKey()));
910
                    break;
911
912
                case ($action instanceof ChangeColumn):
913
                    $instructions->merge($this->getChangeColumnInstructions(
914
                        $table->getName(),
915
                        $action->getColumnName(),
916
                        $action->getColumn()
917
                    ));
918
                    break;
919
920
                case ($action instanceof DropForeignKey && !$action->getForeignKey()->getConstraint()):
0 ignored issues
show
Bug Best Practice introduced by
The expression $action->getForeignKey()->getConstraint() of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
921
                    $instructions->merge($this->getDropForeignKeyByColumnsInstructions(
922
                        $table->getName(),
923
                        $action->getForeignKey()->getColumns()
924
                    ));
925
                    break;
926
927
                case ($action instanceof DropForeignKey && $action->getForeignKey()->getConstraint()):
0 ignored issues
show
Bug Best Practice introduced by
The expression $action->getForeignKey()->getConstraint() of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
928
                    $instructions->merge($this->getDropForeignKeyInstructions(
929
                        $table->getName(),
930
                        $action->getForeignKey()->getConstraint()
931
                    ));
932
                    break;
933
934
                case ($action instanceof DropIndex && $action->getIndex()->getName() !== null):
935
                    $instructions->merge($this->getDropIndexByNameInstructions(
936
                        $table->getName(),
937
                        $action->getIndex()->getName()
938
                    ));
939
                    break;
940
941
                case ($action instanceof DropIndex && $action->getIndex()->getName() == null):
942
                    $instructions->merge($this->getDropIndexByColumnsInstructions(
943
                        $table->getName(),
944
                        $action->getIndex()->getColumns()
945
                    ));
946
                    break;
947
948
                case ($action instanceof DropTable):
949
                    $instructions->merge($this->getDropTableInstructions(
950
                        $table->getName()
951
                    ));
952
                    break;
953
954
                case ($action instanceof RemoveColumn):
955
                    $instructions->merge($this->getDropColumnInstructions(
956
                        $table->getName(),
957
                        $action->getColumn()->getName()
958
                    ));
959
                    break;
960
961
                case ($action instanceof RenameColumn):
962
                    $instructions->merge($this->getRenameColumnInstructions(
963
                        $table->getName(),
964
                        $action->getColumn()->getName(),
965
                        $action->getNewName()
966
                    ));
967
                    break;
968
969
                case ($action instanceof RenameTable):
970
                    $instructions->merge($this->getRenameTableInstructions(
971
                        $table->getName(),
972
                        $action->getNewName()
973
                    ));
974
                    break;
975
976
                case ($action instanceof ChangePrimaryKey):
977
                    $instructions->merge($this->getChangePrimaryKeyInstructions(
978
                        $table,
979
                        $action->getNewColumns()
980
                    ));
981
                    break;
982
983
                case ($action instanceof ChangeComment):
984
                    $instructions->merge($this->getChangeCommentInstructions(
985
                        $table,
986
                        $action->getNewComment()
987
                    ));
988
                    break;
989
990
                default:
991
                    throw new InvalidArgumentException(
992
                        sprintf("Don't know how to execute action: '%s'", get_class($action))
993
                    );
994
            }
995
        }
996
997
        $this->executeAlterSteps($table->getName(), $instructions);
998
    }
999
}
1000