Passed
Pull Request — master (#1928)
by Corey
03:52 queued 01:10
created

PdoAdapter   F

Complexity

Total Complexity 99

Size/Duplication

Total Lines 954
Duplicated Lines 0 %

Test Coverage

Coverage 91.21%

Importance

Changes 7
Bugs 0 Features 1
Metric Value
wmc 99
eloc 317
dl 0
loc 954
c 7
b 0
f 1
ccs 166
cts 182
cp 0.9121
rs 2

45 Methods

Rating   Name   Duplication   Size   Complexity  
D executeActions() 0 104 20
A migrated() 0 33 2
A resetAllBreakpoints() 0 9 1
A setConnection() 0 26 4
A getVersionLog() 0 31 6
A getAttribute() 0 3 1
A dropIndexByName() 0 4 1
A dropForeignKey() 0 9 2
A getVersions() 0 5 1
A getColumnTypes() 0 28 1
A disconnect() 0 2 1
A fetchRow() 0 3 1
A getQueryBuilder() 0 3 1
A dropSchema() 0 3 1
A renameTable() 0 4 1
A addColumn() 0 4 1
A setBreakpoint() 0 3 1
A quoteValue() 0 11 3
A createSchema() 0 3 1
B getDefaultValueDefinition() 0 12 7
A dropTable() 0 4 1
A markBreakpoint() 0 15 1
A execute() 0 10 2
A renameColumn() 0 4 1
A changeComment() 0 4 1
A addForeignKey() 0 4 1
A getConnection() 0 7 2
A unsetBreakpoint() 0 3 1
A dropIndex() 0 4 1
A dropColumn() 0 4 1
A createPdoConnection() 0 26 5
A castToBool() 0 3 2
A connect() 0 2 1
A addIndex() 0 4 1
A bulkinsert() 0 36 5
A fetchAll() 0 3 1
A insert() 0 22 4
A query() 0 3 1
A executeAlterSteps() 0 4 1
A changeColumn() 0 4 1
A setOptions() 0 9 2
A toggleBreakpoint() 0 16 1
A changePrimaryKey() 0 4 1
A quoteString() 0 3 1
A verboseLog() 0 10 3

How to fix   Complexity   

Complex Class

Complex classes like PdoAdapter often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use PdoAdapter, and based on these observations, apply Extract Interface, too.

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
            'attr_errmode' => PDO::ERRMODE_EXCEPTION,
81
        ];
82
83
        try {
84
            $db = new PDO($dsn, $username, $password, $options);
85
86 74
            foreach ($adapterOptions as $key => $option) {
87
                if (strpos($key, 'attr_') === 0) {
88
                    $pdoConstant = '\PDO::' . strtoupper($key);
89
                    if (!defined($pdoConstant)) {
90
                        throw new \UnexpectedValueException('Invalid PDO attribute: ' . $key . ' (' . $pdoConstant . ')');
91
                    }
92
                    $db->setAttribute(constant($pdoConstant), $option);
93 193
                }
94
            }
95
        } catch (PDOException $e) {
96
            throw new InvalidArgumentException(sprintf(
97
                'There was a problem connecting to the database: %s',
98
                $e->getMessage()
99
            ), $e->getCode(), $e);
100
        }
101 191
102
        return $db;
103 191
    }
104 189
105 189
    /**
106 191
     * @inheritDoc
107
     */
108
    public function setOptions(array $options)
109
    {
110
        parent::setOptions($options);
111
112 1
        if (isset($options['connection'])) {
113
            $this->setConnection($options['connection']);
114 1
        }
115
116
        return $this;
117
    }
118
119
    /**
120
     * Sets the database connection.
121
     *
122
     * @param \PDO $connection Connection
123
     *
124
     * @return \Phinx\Db\Adapter\AdapterInterface
125
     */
126 218
    public function setConnection(PDO $connection)
127
    {
128 218
        $this->connection = $connection;
129 3
130 3
        // Create the schema table if it doesn't already exist
131
        if (!$this->hasSchemaTable()) {
132
            $this->createSchemaTable();
133 217
        } else {
134
            $table = new DbTable($this->getSchemaTableName(), [], $this);
135
            if (!$table->hasColumn('migration_name')) {
136
                $table
137
                    ->addColumn(
138
                        'migration_name',
139
                        'string',
140
                        ['limit' => 100, 'after' => 'version', 'default' => null, 'null' => true]
141
                    )
142 220
                    ->save();
143
            }
144 220
            if (!$table->hasColumn('breakpoint')) {
145
                $table
146
                    ->addColumn('breakpoint', 'boolean', ['default' => false])
147
                    ->save();
148
            }
149
        }
150 151
151
        return $this;
152 151
    }
153 151
154
    /**
155
     * Gets the database connection
156
     *
157
     * @return \PDO
158
     */
159 213
    public function getConnection()
160
    {
161 213
        if ($this->connection === null) {
162 213
            $this->connect();
163 213
        }
164 208
165 208
        return $this->connection;
166 213
    }
167
168
    /**
169
     * @inheritDoc
170
     */
171
    public function connect()
172 1
    {
173
    }
174 1
175 1
    /**
176 1
     * @inheritDoc
177 1
     */
178
    public function disconnect()
179 1
    {
180 1
    }
181 1
182
    /**
183 1
     * @inheritDoc
184 1
     */
185 1
    public function execute($sql)
186
    {
187
        $sql = rtrim($sql, "; \t\n\r\0\x0B") . ';';
188
        $this->verboseLog($sql);
189
190 11
        if ($this->isDryRunEnabled()) {
191
            return 0;
192 11
        }
193 11
194 11
        return $this->getConnection()->exec($sql);
195 11
    }
196
197 11
    /**
198 11
     * Returns the Cake\Database connection object using the same underlying
199 11
     * PDO object as this connection.
200
     *
201 11
     * @return \Cake\Database\Connection
202 11
     */
203 11
    abstract public function getDecoratedConnection();
204 11
205 11
    /**
206 11
     * @inheritDoc
207
     */
208 11
    public function getQueryBuilder()
209 11
    {
210
        return $this->getDecoratedConnection()->newQuery();
211 11
    }
212 11
213 11
    /**
214
     * Executes a query and returns PDOStatement.
215 11
     *
216 11
     * @param string $sql SQL
217 11
     *
218
     * @return \PDOStatement
219
     */
220
    public function query($sql)
221
    {
222 5
        return $this->getConnection()->query($sql);
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->getConnection()->query($sql) could also return false which is incompatible with the documented return type PDOStatement. Did you maybe forget to handle an error condition?

If the returned type also contains false, it is an indicator that maybe an error condition leading to the specific return statement remains unhandled.

Loading history...
223
    }
224 5
225
    /**
226 5
     * @inheritDoc
227
     */
228
    public function fetchRow($sql)
229
    {
230
        return $this->query($sql)->fetch();
231
    }
232 8
233
    /**
234 8
     * @inheritDoc
235
     */
236 8
    public function fetchAll($sql)
237 8
    {
238 6
        return $this->query($sql)->fetchAll();
239 6
    }
240 2
241 1
    /**
242 1
     * @inheritDoc
243 1
     */
244 1
    public function insert(Table $table, $row)
245 8
    {
246
        $sql = sprintf(
247 7
            'INSERT INTO %s ',
248 7
            $this->quoteTableName($table->getName())
249 7
        );
250 7
        $columns = array_keys($row);
251
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $columns)) . ')';
252 7
253
        foreach ($row as $column => $value) {
254
            if (is_bool($value)) {
255
                $row[$column] = $this->castToBool($value);
256
            }
257
        }
258 5
259
        if ($this->isDryRunEnabled()) {
260 5
            $sql .= ' VALUES (' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ');';
261
            $this->output->writeln($sql);
262 5
        } else {
263 5
            $sql .= ' VALUES (' . implode(', ', array_fill(0, count($columns), '?')) . ')';
264 5
            $stmt = $this->getConnection()->prepare($sql);
265 5
            $stmt->execute(array_values($row));
266 5
        }
267 5
    }
268 5
269 5
    /**
270 5
     * Quotes a database value.
271 5
     *
272 5
     * @param mixed $value The value to quote
273 5
     *
274 5
     * @return mixed
275 5
     */
276
    protected function quoteValue($value)
277 5
    {
278 5
        if (is_numeric($value)) {
279
            return $value;
280 3
        }
281 3
282 3
        if ($value === null) {
283 3
            return 'null';
284 3
        }
285 3
286
        return $this->getConnection()->quote($value);
287 3
    }
288
289
    /**
290 5
     * Quotes a database string.
291
     *
292
     * @param string $value The string to quote
293
     *
294
     * @return string
295
     */
296 1
    protected function quoteString($value)
297
    {
298 1
        return $this->getConnection()->quote($value);
299 1
    }
300 1
301 1
    /**
302 1
     * @inheritDoc
303 1
     */
304 1
    public function bulkinsert(Table $table, $rows)
305 1
    {
306 1
        $sql = sprintf(
307 1
            'INSERT INTO %s ',
308 1
            $this->quoteTableName($table->getName())
309 1
        );
310
        $current = current($rows);
311 1
        $keys = array_keys($current);
312
        $sql .= '(' . implode(', ', array_map([$this, 'quoteColumnName'], $keys)) . ') VALUES ';
313
314
        if ($this->isDryRunEnabled()) {
315
            $values = array_map(function ($row) {
316
                return '(' . implode(', ', array_map([$this, 'quoteValue'], $row)) . ')';
317 1
            }, $rows);
318
            $sql .= implode(', ', $values) . ';';
319 1
            $this->output->writeln($sql);
320 1
        } else {
321 1
            $count_keys = count($keys);
322 1
            $query = '(' . implode(', ', array_fill(0, $count_keys, '?')) . ')';
323 1
            $count_vars = count($rows);
324 1
            $queries = array_fill(0, $count_vars, $query);
325 1
            $sql .= implode(',', $queries);
326 1
            $stmt = $this->getConnection()->prepare($sql);
327 1
            $vals = [];
328
329
            foreach ($rows as $row) {
330
                foreach ($row as $v) {
331
                    if (is_bool($v)) {
332
                        $vals[] = $this->castToBool($v);
333
                    } else {
334
                        $vals[] = $v;
335
                    }
336
                }
337
            }
338
339
            $stmt->execute($vals);
340
        }
341
    }
342
343
    /**
344
     * @inheritDoc
345
     */
346
    public function getVersions()
347
    {
348
        $rows = $this->getVersionLog();
349 208
350
        return array_keys($rows);
351
    }
352 208
353 208
    /**
354 208
     * {@inheritDoc}
355 208
     *
356 208
     * @throws \RuntimeException
357 208
     */
358 208
    public function getVersionLog()
359 208
    {
360 208
        $result = [];
361 208
362 208
        switch ($this->options['version_order']) {
363 208
            case Config::VERSION_ORDER_CREATION_TIME:
364 208
                $orderBy = 'version ASC';
365 208
                break;
366 208
            case Config::VERSION_ORDER_EXECUTION_TIME:
367 208
                $orderBy = 'start_time ASC, version ASC';
368
                break;
369 208
            default:
370 208
                throw new RuntimeException('Invalid version_order configuration option');
371 208
        }
372 208
373 208
        // This will throw an exception if doing a --dry-run without any migrations as phinxlog
374
        // does not exist, so in that case, we can just expect to trivially return empty set
375
        try {
376
            $rows = $this->fetchAll(sprintf('SELECT * FROM %s ORDER BY %s', $this->quoteTableName($this->getSchemaTableName()), $orderBy));
377
        } catch (PDOException $e) {
378
            if (!$this->isDryRunEnabled()) {
379 121
                throw $e;
380
            }
381 121
            $rows = [];
382
        }
383
384
        foreach ($rows as $version) {
385
            $result[$version['version']] = $version;
386
        }
387
388
        return $result;
389
    }
390
391
    /**
392
     * @inheritDoc
393
     */
394
    public function migrated(MigrationInterface $migration, $direction, $startTime, $endTime)
395
    {
396
        if (strcasecmp($direction, MigrationInterface::UP) === 0) {
397
            // up
398
            $sql = sprintf(
399
                "INSERT INTO %s (%s, %s, %s, %s, %s) VALUES ('%s', '%s', '%s', '%s', %s);",
400
                $this->quoteTableName($this->getSchemaTableName()),
401
                $this->quoteColumnName('version'),
402
                $this->quoteColumnName('migration_name'),
403
                $this->quoteColumnName('start_time'),
404
                $this->quoteColumnName('end_time'),
405
                $this->quoteColumnName('breakpoint'),
406
                $migration->getVersion(),
407
                substr($migration->getName(), 0, 100),
408
                $startTime,
409
                $endTime,
410
                $this->castToBool(false)
411
            );
412
413
            $this->execute($sql);
414
        } else {
415
            // down
416
            $sql = sprintf(
417
                "DELETE FROM %s WHERE %s = '%s'",
418
                $this->quoteTableName($this->getSchemaTableName()),
419
                $this->quoteColumnName('version'),
420
                $migration->getVersion()
421
            );
422
423
            $this->execute($sql);
424
        }
425
426
        return $this;
427
    }
428
429
    /**
430
     * @inheritDoc
431
     */
432
    public function toggleBreakpoint(MigrationInterface $migration)
433
    {
434
        $this->query(
435
            sprintf(
436
                '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\';',
437
                $this->quoteTableName($this->getSchemaTableName()),
438
                $this->quoteColumnName('breakpoint'),
439
                $this->castToBool(true),
440
                $this->castToBool(false),
441
                $this->quoteColumnName('version'),
442
                $migration->getVersion(),
443
                $this->quoteColumnName('start_time')
444
            )
445
        );
446
447
        return $this;
448
    }
449
450
    /**
451
     * @inheritDoc
452
     */
453
    public function resetAllBreakpoints()
454
    {
455
        return $this->execute(
456
            sprintf(
457
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %2$s <> %3$s;',
458
                $this->quoteTableName($this->getSchemaTableName()),
459
                $this->quoteColumnName('breakpoint'),
460
                $this->castToBool(false),
461
                $this->quoteColumnName('start_time')
462
            )
463
        );
464
    }
465
466
    /**
467
     * @inheritDoc
468
     */
469
    public function setBreakpoint(MigrationInterface $migration)
470
    {
471
        return $this->markBreakpoint($migration, true);
472
    }
473
474
    /**
475
     * @inheritDoc
476
     */
477
    public function unsetBreakpoint(MigrationInterface $migration)
478
    {
479
        return $this->markBreakpoint($migration, false);
480
    }
481
482
    /**
483
     * Mark a migration breakpoint.
484
     *
485
     * @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint
486
     * @param bool $state The required state of the breakpoint
487
     *
488
     * @return \Phinx\Db\Adapter\AdapterInterface
489
     */
490
    protected function markBreakpoint(MigrationInterface $migration, $state)
491
    {
492
        $this->query(
493
            sprintf(
494
                'UPDATE %1$s SET %2$s = %3$s, %4$s = %4$s WHERE %5$s = \'%6$s\';',
495
                $this->quoteTableName($this->getSchemaTableName()),
496
                $this->quoteColumnName('breakpoint'),
497
                $this->castToBool($state),
498
                $this->quoteColumnName('start_time'),
499
                $this->quoteColumnName('version'),
500
                $migration->getVersion()
501
            )
502
        );
503
504
        return $this;
505
    }
506
507
    /**
508
     * {@inheritDoc}
509
     *
510
     * @throws \BadMethodCallException
511
     *
512
     * @return void
513
     */
514
    public function createSchema($schemaName = 'public')
515
    {
516
        throw new BadMethodCallException('Creating a schema is not supported');
517
    }
518
519
    /**
520
     * {@inheritDoc}
521
     *
522
     * @throws \BadMethodCallException
523
     *
524
     * @return void
525
     */
526
    public function dropSchema($name)
527
    {
528
        throw new BadMethodCallException('Dropping a schema is not supported');
529
    }
530
531
    /**
532
     * @inheritDoc
533
     */
534
    public function getColumnTypes()
535
    {
536
        return [
537
            'string',
538
            'char',
539
            'text',
540
            'tinyinteger',
541
            'smallinteger',
542
            'integer',
543
            'biginteger',
544
            'bit',
545
            'float',
546
            'decimal',
547
            'double',
548
            'datetime',
549
            'timestamp',
550
            'time',
551
            'date',
552
            'blob',
553
            'binary',
554
            'varbinary',
555
            'boolean',
556
            'uuid',
557
            // Geospatial data types
558
            'geometry',
559
            'point',
560
            'linestring',
561
            'polygon',
562
        ];
563
    }
564
565
    /**
566
     * @inheritDoc
567
     */
568
    public function castToBool($value)
569
    {
570
        return (bool)$value ? 1 : 0;
571
    }
572
573
    /**
574
     * Retrieve a database connection attribute
575
     *
576
     * @see http://php.net/manual/en/pdo.getattribute.php
577
     *
578
     * @param int $attribute One of the PDO::ATTR_* constants
579
     *
580
     * @return mixed
581
     */
582
    public function getAttribute($attribute)
583
    {
584
        return $this->connection->getAttribute($attribute);
0 ignored issues
show
Bug introduced by
The method getAttribute() does not exist on null. ( Ignorable by Annotation )

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

584
        return $this->connection->/** @scrutinizer ignore-call */ getAttribute($attribute);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
585
    }
586
587
    /**
588
     * Get the definition for a `DEFAULT` statement.
589
     *
590
     * @param mixed $default Default value
591
     * @param string|null $columnType column type added
592
     *
593
     * @return string
594
     */
595
    protected function getDefaultValueDefinition($default, $columnType = null)
596
    {
597
        // Ensure a defaults of CURRENT_TIMESTAMP(3) is not quoted.
598
        if (is_string($default) && strpos($default, 'CURRENT_TIMESTAMP') !== 0) {
599
            $default = $this->getConnection()->quote($default);
600
        } elseif (is_bool($default)) {
601
            $default = $this->castToBool($default);
602
        } elseif ($default !== null && $columnType === static::PHINX_TYPE_BOOLEAN) {
603
            $default = $this->castToBool((bool)$default);
604
        }
605
606
        return isset($default) ? " DEFAULT $default" : '';
607
    }
608
609
    /**
610
     * Executes all the ALTER TABLE instructions passed for the given table
611
     *
612
     * @param string $tableName The table name to use in the ALTER statement
613
     * @param \Phinx\Db\Util\AlterInstructions $instructions The object containing the alter sequence
614
     *
615
     * @return void
616
     */
617
    protected function executeAlterSteps($tableName, AlterInstructions $instructions)
618
    {
619
        $alter = sprintf('ALTER TABLE %s %%s', $this->quoteTableName($tableName));
620
        $instructions->execute($alter, [$this, 'execute']);
621
    }
622
623
    /**
624
     * @inheritDoc
625
     */
626
    public function addColumn(Table $table, Column $column)
627
    {
628
        $instructions = $this->getAddColumnInstructions($table, $column);
629
        $this->executeAlterSteps($table->getName(), $instructions);
630
    }
631
632
    /**
633
     * Returns the instructions to add the specified column to a database table.
634
     *
635
     * @param \Phinx\Db\Table\Table $table Table
636
     * @param \Phinx\Db\Table\Column $column Column
637
     *
638
     * @return \Phinx\Db\Util\AlterInstructions
639
     */
640
    abstract protected function getAddColumnInstructions(Table $table, Column $column);
641
642
    /**
643
     * @inheritdoc
644
     */
645
    public function renameColumn($tableName, $columnName, $newColumnName)
646
    {
647
        $instructions = $this->getRenameColumnInstructions($tableName, $columnName, $newColumnName);
648
        $this->executeAlterSteps($tableName, $instructions);
649
    }
650
651
    /**
652
     * Returns the instructions to rename the specified column.
653
     *
654
     * @param string $tableName Table name
655
     * @param string $columnName Column Name
656
     * @param string $newColumnName New Column Name
657
     *
658
     * @return \Phinx\Db\Util\AlterInstructions
659
     */
660
    abstract protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName);
661
662
    /**
663
     * @inheritdoc
664
     */
665
    public function changeColumn($tableName, $columnName, Column $newColumn)
666
    {
667
        $instructions = $this->getChangeColumnInstructions($tableName, $columnName, $newColumn);
668
        $this->executeAlterSteps($tableName, $instructions);
669
    }
670
671
    /**
672
     * Returns the instructions to change a table column type.
673
     *
674
     * @param string $tableName Table name
675
     * @param string $columnName Column Name
676
     * @param \Phinx\Db\Table\Column $newColumn New Column
677
     *
678
     * @return \Phinx\Db\Util\AlterInstructions
679
     */
680
    abstract protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn);
681
682
    /**
683
     * @inheritdoc
684
     */
685
    public function dropColumn($tableName, $columnName)
686
    {
687
        $instructions = $this->getDropColumnInstructions($tableName, $columnName);
688
        $this->executeAlterSteps($tableName, $instructions);
689
    }
690
691
    /**
692
     * Returns the instructions to drop the specified column.
693
     *
694
     * @param string $tableName Table name
695
     * @param string $columnName Column Name
696
     *
697
     * @return \Phinx\Db\Util\AlterInstructions
698
     */
699
    abstract protected function getDropColumnInstructions($tableName, $columnName);
700
701
    /**
702
     * @inheritdoc
703
     */
704
    public function addIndex(Table $table, Index $index)
705
    {
706
        $instructions = $this->getAddIndexInstructions($table, $index);
707
        $this->executeAlterSteps($table->getName(), $instructions);
708
    }
709
710
    /**
711
     * Returns the instructions to add the specified index to a database table.
712
     *
713
     * @param \Phinx\Db\Table\Table $table Table
714
     * @param \Phinx\Db\Table\Index $index Index
715
     *
716
     * @return \Phinx\Db\Util\AlterInstructions
717
     */
718
    abstract protected function getAddIndexInstructions(Table $table, Index $index);
719
720
    /**
721
     * @inheritdoc
722
     */
723
    public function dropIndex($tableName, $columns)
724
    {
725
        $instructions = $this->getDropIndexByColumnsInstructions($tableName, $columns);
726
        $this->executeAlterSteps($tableName, $instructions);
727
    }
728
729
    /**
730
     * Returns the instructions to drop the specified index from a database table.
731
     *
732
     * @param string $tableName The name of of the table where the index is
733
     * @param mixed $columns Column(s)
734
     *
735
     * @return \Phinx\Db\Util\AlterInstructions
736
     */
737
    abstract protected function getDropIndexByColumnsInstructions($tableName, $columns);
738
739
    /**
740
     * @inheritdoc
741
     */
742
    public function dropIndexByName($tableName, $indexName)
743
    {
744
        $instructions = $this->getDropIndexByNameInstructions($tableName, $indexName);
745
        $this->executeAlterSteps($tableName, $instructions);
746
    }
747
748
    /**
749
     * Returns the instructions to drop the index specified by name from a database table.
750
     *
751
     * @param string $tableName The table name whe the index is
752
     * @param string $indexName The name of the index
753
     *
754
     * @return \Phinx\Db\Util\AlterInstructions
755
     */
756
    abstract protected function getDropIndexByNameInstructions($tableName, $indexName);
757
758
    /**
759
     * @inheritdoc
760
     */
761
    public function addForeignKey(Table $table, ForeignKey $foreignKey)
762
    {
763
        $instructions = $this->getAddForeignKeyInstructions($table, $foreignKey);
764
        $this->executeAlterSteps($table->getName(), $instructions);
765
    }
766
767
    /**
768
     * Returns the instructions to adds the specified foreign key to a database table.
769
     *
770
     * @param \Phinx\Db\Table\Table $table The table to add the constraint to
771
     * @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add
772
     * @return \Phinx\Db\Util\AlterInstructions
773
     */
774
    abstract protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey);
775
776
    /**
777
     * @inheritDoc
778
     */
779
    public function dropForeignKey($tableName, $columns, $constraint = null)
780
    {
781
        if ($constraint) {
782
            $instructions = $this->getDropForeignKeyInstructions($tableName, $constraint);
783
        } else {
784
            $instructions = $this->getDropForeignKeyByColumnsInstructions($tableName, $columns);
785
        }
786
787
        $this->executeAlterSteps($tableName, $instructions);
788
    }
789
790
    /**
791
     * Returns the instructions to drop the specified foreign key from a database table.
792
     *
793
     * @param string $tableName The table where the foreign key constraint is
794
     * @param string $constraint Constraint name
795
     *
796
     * @return \Phinx\Db\Util\AlterInstructions
797
     */
798
    abstract protected function getDropForeignKeyInstructions($tableName, $constraint);
799
800
    /**
801
     * Returns the instructions to drop the specified foreign key from a database table.
802
     *
803
     * @param string $tableName The table where the foreign key constraint is
804
     * @param string[] $columns The list of column names
805
     *
806
     * @return \Phinx\Db\Util\AlterInstructions
807
     */
808
    abstract protected function getDropForeignKeyByColumnsInstructions($tableName, $columns);
809
810
    /**
811
     * @inheritdoc
812
     */
813
    public function dropTable($tableName)
814
    {
815
        $instructions = $this->getDropTableInstructions($tableName);
816
        $this->executeAlterSteps($tableName, $instructions);
817
    }
818
819
    /**
820
     * Returns the instructions to drop the specified database table.
821
     *
822
     * @param string $tableName Table name
823
     *
824
     * @return \Phinx\Db\Util\AlterInstructions
825
     */
826
    abstract protected function getDropTableInstructions($tableName);
827
828
    /**
829
     * @inheritdoc
830
     */
831
    public function renameTable($tableName, $newTableName)
832
    {
833
        $instructions = $this->getRenameTableInstructions($tableName, $newTableName);
834
        $this->executeAlterSteps($tableName, $instructions);
835
    }
836
837
    /**
838
     * Returns the instructions to rename the specified database table.
839
     *
840
     * @param string $tableName Table name
841
     * @param string $newTableName New Name
842
     *
843
     * @return \Phinx\Db\Util\AlterInstructions
844
     */
845
    abstract protected function getRenameTableInstructions($tableName, $newTableName);
846
847
    /**
848
     * @inheritdoc
849
     */
850
    public function changePrimaryKey(Table $table, $newColumns)
851
    {
852
        $instructions = $this->getChangePrimaryKeyInstructions($table, $newColumns);
853
        $this->executeAlterSteps($table->getName(), $instructions);
854
    }
855
856
    /**
857
     * Returns the instructions to change the primary key for the specified database table.
858
     *
859
     * @param \Phinx\Db\Table\Table $table Table
860
     * @param string|string[]|null $newColumns Column name(s) to belong to the primary key, or null to drop the key
861
     *
862
     * @return \Phinx\Db\Util\AlterInstructions
863
     */
864
    abstract protected function getChangePrimaryKeyInstructions(Table $table, $newColumns);
865
866
    /**
867
     * @inheritdoc
868
     */
869
    public function changeComment(Table $table, $newComment)
870
    {
871
        $instructions = $this->getChangeCommentInstructions($table, $newComment);
872
        $this->executeAlterSteps($table->getName(), $instructions);
873
    }
874
875
    /**
876
     * Returns the instruction to change the comment for the specified database table.
877
     *
878
     * @param \Phinx\Db\Table\Table $table Table
879
     * @param string|null $newComment New comment string, or null to drop the comment
880
     *
881
     * @return \Phinx\Db\Util\AlterInstructions
882
     */
883
    abstract protected function getChangeCommentInstructions(Table $table, $newComment);
884
885
    /**
886
     * {@inheritDoc}
887
     *
888
     * @throws \InvalidArgumentException
889
     *
890
     * @return void
891
     */
892
    public function executeActions(Table $table, array $actions)
893
    {
894
        $instructions = new AlterInstructions();
895
896
        foreach ($actions as $action) {
897
            switch (true) {
898
                case ($action instanceof AddColumn):
899
                    $instructions->merge($this->getAddColumnInstructions($table, $action->getColumn()));
900
                    break;
901
902
                case ($action instanceof AddIndex):
903
                    $instructions->merge($this->getAddIndexInstructions($table, $action->getIndex()));
904
                    break;
905
906
                case ($action instanceof AddForeignKey):
907
                    $instructions->merge($this->getAddForeignKeyInstructions($table, $action->getForeignKey()));
908
                    break;
909
910
                case ($action instanceof ChangeColumn):
911
                    $instructions->merge($this->getChangeColumnInstructions(
912
                        $table->getName(),
913
                        $action->getColumnName(),
914
                        $action->getColumn()
915
                    ));
916
                    break;
917
918
                case ($action instanceof DropForeignKey && !$action->getForeignKey()->getConstraint()):
919
                    $instructions->merge($this->getDropForeignKeyByColumnsInstructions(
920
                        $table->getName(),
921
                        $action->getForeignKey()->getColumns()
922
                    ));
923
                    break;
924
925
                case ($action instanceof DropForeignKey && $action->getForeignKey()->getConstraint()):
926
                    $instructions->merge($this->getDropForeignKeyInstructions(
927
                        $table->getName(),
928
                        $action->getForeignKey()->getConstraint()
929
                    ));
930
                    break;
931
932
                case ($action instanceof DropIndex && $action->getIndex()->getName() !== null):
933
                    $instructions->merge($this->getDropIndexByNameInstructions(
934
                        $table->getName(),
935
                        $action->getIndex()->getName()
936
                    ));
937
                    break;
938
939
                case ($action instanceof DropIndex && $action->getIndex()->getName() == null):
0 ignored issues
show
Bug introduced by
It seems like you are loosely comparing $action->getIndex()->getName() of type null|string against null; this is ambiguous if the string can be empty. Consider using a strict comparison === instead.
Loading history...
940
                    $instructions->merge($this->getDropIndexByColumnsInstructions(
941
                        $table->getName(),
942
                        $action->getIndex()->getColumns()
943
                    ));
944
                    break;
945
946
                case ($action instanceof DropTable):
947
                    $instructions->merge($this->getDropTableInstructions(
948
                        $table->getName()
949
                    ));
950
                    break;
951
952
                case ($action instanceof RemoveColumn):
953
                    $instructions->merge($this->getDropColumnInstructions(
954
                        $table->getName(),
955
                        $action->getColumn()->getName()
956
                    ));
957
                    break;
958
959
                case ($action instanceof RenameColumn):
960
                    $instructions->merge($this->getRenameColumnInstructions(
961
                        $table->getName(),
962
                        $action->getColumn()->getName(),
963
                        $action->getNewName()
964
                    ));
965
                    break;
966
967
                case ($action instanceof RenameTable):
968
                    $instructions->merge($this->getRenameTableInstructions(
969
                        $table->getName(),
970
                        $action->getNewName()
971
                    ));
972
                    break;
973
974
                case ($action instanceof ChangePrimaryKey):
975
                    $instructions->merge($this->getChangePrimaryKeyInstructions(
976
                        $table,
977
                        $action->getNewColumns()
978
                    ));
979
                    break;
980
981
                case ($action instanceof ChangeComment):
982
                    $instructions->merge($this->getChangeCommentInstructions(
983
                        $table,
984
                        $action->getNewComment()
985
                    ));
986
                    break;
987
988
                default:
989
                    throw new InvalidArgumentException(
990
                        sprintf("Don't know how to execute action: '%s'", get_class($action))
991
                    );
992
            }
993
        }
994
995
        $this->executeAlterSteps($table->getName(), $instructions);
996
    }
997
}
998