diff --git a/.stickler.yml b/.stickler.yml index b74644c99..53f73a083 100644 --- a/.stickler.yml +++ b/.stickler.yml @@ -1,6 +1,12 @@ linters: phpcs: standard: CakePHP + fixer: true + files: ignore: - 'vendor/*' + +fixers: + enable: true + workflow: commit diff --git a/.travis.yml b/.travis.yml index 6b01f4417..b140df6b4 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,8 +6,6 @@ addons: postgresql: "9.2" php: - - 5.4 - - 5.5 - 5.6 - 7.0 - 7.1 diff --git a/composer.json b/composer.json index b79df277e..544482d64 100644 --- a/composer.json +++ b/composer.json @@ -26,12 +26,13 @@ }], "require": { "php": ">=5.4", + "cakephp/collection": "^3.5", "symfony/console": "^2.8|^3.0|^4.0", "symfony/config": "^2.8|^3.0|^4.0", "symfony/yaml": "^2.8|^3.0|^4.0" }, "require-dev": { - "phpunit/phpunit": "^4.8.35|^5.7|^6.5", + "phpunit/phpunit": ">=5.7", "sebastian/comparator": ">=1.2.3", "cakephp/cakephp-codesniffer": "^3.0" }, diff --git a/src/Phinx/Db/Action/Action.php b/src/Phinx/Db/Action/Action.php new file mode 100644 index 000000000..d8b05eb97 --- /dev/null +++ b/src/Phinx/Db/Action/Action.php @@ -0,0 +1,56 @@ +table = $table; + } + + /** + * The table this action will be applied to + * + * @return \Phinx\Db\Table\Table + */ + public function getTable() + { + return $this->table; + } +} diff --git a/src/Phinx/Db/Action/AddColumn.php b/src/Phinx/Db/Action/AddColumn.php new file mode 100644 index 000000000..2ee0d0f0e --- /dev/null +++ b/src/Phinx/Db/Action/AddColumn.php @@ -0,0 +1,80 @@ +column = $column; + } + + /** + * Returns a new AddColumn object after assembling the given commands + * + * @param Table $table The table to add the column to + * @param mixed $columnName The column name + * @param mixed $type The column type + * @param mixed $options The column options + * @return AddColumn + */ + public static function build(Table $table, $columnName, $type = null, $options = []) + { + $column = new Column(); + $column->setName($columnName); + $column->setType($type); + $column->setOptions($options); // map options to column methods + + return new static($table, $column); + } + + /** + * Returns the column to be added + * + * @return Column + */ + public function getColumn() + { + return $this->column; + } +} diff --git a/src/Phinx/Db/Action/AddForeignKey.php b/src/Phinx/Db/Action/AddForeignKey.php new file mode 100644 index 000000000..f33f8db72 --- /dev/null +++ b/src/Phinx/Db/Action/AddForeignKey.php @@ -0,0 +1,97 @@ +foreignKey = $fk; + } + + /** + * Creats a new AddForeignKey object after building the foreign key with + * the passed attibutes + * + * @param Table $table The table object to add the foreign key to + * @param string|string[] $columns The columns for the foreign key + * @param Table|string $referencedTable The table the foreign key references + * @param string $referencedColumns The columns in the referenced table + * @param array $options Extra options for the foreign key + * @param string|null $name The name of the foreing key + * @return AddForeignKey + */ + public static function build(Table $table, $columns, $referencedTable, $referencedColumns = ['id'], array $options = [], $name = null) + { + if (is_string($referencedColumns)) { + $referencedColumns = [$referencedColumns]; // str to array + } + + if (is_string($referencedTable)) { + $referencedTable = new Table($referencedTable); + } + + $fk = new ForeignKey(); + $fk->setReferencedTable($referencedTable) + ->setColumns($columns) + ->setReferencedColumns($referencedColumns) + ->setOptions($options); + + if ($name !== null) { + $fk->setConstraint($name); + } + + return new static($table, $fk); + } + + /** + * Returns the foreign key to be added + * + * @return ForeignKey + */ + public function getForeignKey() + { + return $this->foreignKey; + } +} diff --git a/src/Phinx/Db/Action/AddIndex.php b/src/Phinx/Db/Action/AddIndex.php new file mode 100644 index 000000000..6db89d2a4 --- /dev/null +++ b/src/Phinx/Db/Action/AddIndex.php @@ -0,0 +1,89 @@ +index = $index; + } + + /** + * Creates a new AddIndex object after building the index object with the + * provided arguments + * + * @param Table $table The table to add the index to + * @param mixed $columns The columns to index + * @param array $options Additional options for the index creation + * @return AddIndex + */ + public static function build(Table $table, $columns, array $options = []) + { + // create a new index object if strings or an array of strings were supplied + $index = $columns; + + if (!$columns instanceof Index) { + $index = new Index(); + + if (is_string($columns)) { + $columns = [$columns]; // str to array + } + + $index->setColumns($columns); + $index->setOptions($options); + } + + return new static($table, $index); + } + + /** + * Returns the index to be added + * + * @return Index + */ + public function getIndex() + { + return $this->index; + } +} diff --git a/src/Phinx/Db/Action/ChangeColumn.php b/src/Phinx/Db/Action/ChangeColumn.php new file mode 100644 index 000000000..82335a6e0 --- /dev/null +++ b/src/Phinx/Db/Action/ChangeColumn.php @@ -0,0 +1,104 @@ +columnName = $columnName; + $this->column = $column; + + // if the name was omitted use the existing column name + if ($column->getName() === null || strlen($column->getName()) === 0) { + $column->setName($columnName); + } + } + + /** + * Creates a new ChangeColumn object after building the column definition + * out of the provided arguments + * + * @param Table $table The table to alter + * @param mixed $columnName The name of the column to change + * @param mixed $type The type of the column + * @param mixed $options Addiotional options for the column + * @return ChangeColumn + */ + public static function build(Table $table, $columnName, $type = null, $options = []) + { + $column = new Column(); + $column->setName($columnName); + $column->setType($type); + $column->setOptions($options); // map options to column methods + + return new static($table, $columnName, $column); + } + + /** + * Returns the name of the column to change + * + * @return string + */ + public function getColumnName() + { + return $this->columnName; + } + + /** + * Returns the column definition + * + * @return Column + */ + public function getColumn() + { + return $this->column; + } +} diff --git a/src/Phinx/Db/Action/CreateTable.php b/src/Phinx/Db/Action/CreateTable.php new file mode 100644 index 000000000..60f8b0ad2 --- /dev/null +++ b/src/Phinx/Db/Action/CreateTable.php @@ -0,0 +1,31 @@ +foreignKey = $foreignKey; + } + + /** + * Creates a new DropForeignKey object after building the ForeignKey + * definition out of the passed arguments. + * + * @param Table $table The table to dele the foreign key from + * @param string|string[] $columns The columns participating in the foreign key + * @param string|null $constraint The constraint name + * @return DropForeignKey + */ + public static function build(Table $table, $columns, $constraint = null) + { + if (is_string($columns)) { + $columns = [$columns]; + } + + $foreignKey = new ForeignKey(); + $foreignKey->setColumns($columns); + + if ($constraint) { + $foreignKey->setConstraint($constraint); + } + + return new static($table, $foreignKey); + } + + /** + * Returns the foreign key to remove + * + * @return ForeignKey + */ + public function getForeignKey() + { + return $this->foreignKey; + } +} diff --git a/src/Phinx/Db/Action/DropIndex.php b/src/Phinx/Db/Action/DropIndex.php new file mode 100644 index 000000000..8bdb52b4f --- /dev/null +++ b/src/Phinx/Db/Action/DropIndex.php @@ -0,0 +1,94 @@ +index = $index; + } + + /** + * Creates a new DropIndex object after assembling the passed + * arguments. + * + * @param Table $table The table where the index is + * @param array $columns the indexed columns + * @return DropIndex + */ + public static function build(Table $table, array $columns = []) + { + $index = new Index(); + $index->setColumns($columns); + + return new static($table, $index); + } + + /** + * Creates a new DropIndex when the name of the index to drop + * is knonwn. + * + * @param Table $table The table where the index is + * @param mixed $name The name of the index + * @return DropIndex + */ + public static function buildFromName(Table $table, $name) + { + $index = new Index(); + $index->setName($name); + + return new static($table, $index); + } + + /** + * Returns the index to be dropped + * + * @return Index + */ + public function getIndex() + { + return $this->index; + } +} diff --git a/src/Phinx/Db/Action/DropTable.php b/src/Phinx/Db/Action/DropTable.php new file mode 100644 index 000000000..03c3eb09e --- /dev/null +++ b/src/Phinx/Db/Action/DropTable.php @@ -0,0 +1,31 @@ +column = $column; + } + + /** + * Creates a new RemoveColumn object after assembling the + * passed arguments. + * + * @param Table $table The table where the column is + * @param mixed $columnName The name of the column to drop + * @return RemoveColumn + */ + public static function build(Table $table, $columnName) + { + $column = new Column(); + $column->setName($columnName); + + return new static($table, $column); + } + + /** + * Returns the column to be dropped + * + * @return Column + */ + public function getColumn() + { + return $this->column; + } +} diff --git a/src/Phinx/Db/Action/RenameColumn.php b/src/Phinx/Db/Action/RenameColumn.php new file mode 100644 index 000000000..533e3c28f --- /dev/null +++ b/src/Phinx/Db/Action/RenameColumn.php @@ -0,0 +1,97 @@ +newName = $newName; + $this->column = $column; + } + + /** + * Creates a new RenameColumn object after building the passed + * arguments + * + * @param Table $table The table where the column is + * @param mixed $columnName The name of the column to be changed + * @param mixed $newName The new name for the column + * @return RenameColumn + */ + public static function build(Table $table, $columnName, $newName) + { + $column = new Column(); + $column->setName($columnName); + + return new static($table, $column, $newName); + } + + /** + * Returns the column to be changed + * + * @return Column + */ + public function getColumn() + { + return $this->column; + } + + /** + * Returns the new name for the column + * + * @return string + */ + public function getNewName() + { + return $this->newName; + } +} diff --git a/src/Phinx/Db/Action/RenameTable.php b/src/Phinx/Db/Action/RenameTable.php new file mode 100644 index 000000000..df3d58c20 --- /dev/null +++ b/src/Phinx/Db/Action/RenameTable.php @@ -0,0 +1,60 @@ +newName = $newName; + } + + /** + * Return the new name for the table + * + * @return string + */ + public function getNewName() + { + return $this->newName; + } +} diff --git a/src/Phinx/Db/Adapter/AdapterInterface.php b/src/Phinx/Db/Adapter/AdapterInterface.php index 7ca228318..caa8302f9 100644 --- a/src/Phinx/Db/Adapter/AdapterInterface.php +++ b/src/Phinx/Db/Adapter/AdapterInterface.php @@ -28,10 +28,10 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; use Phinx\Migration\MigrationInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -257,6 +257,15 @@ public function rollbackTransaction(); */ public function execute($sql); + /** + * Executes a list of migration actions for the given table + * + * @param \Phinx\Db\Table\Table $table The table to execute the actions for + * @param \Phinx\Db\Action\Action[] $actions The table to execute the actions for + * @return void + */ + public function executeActions(Table $table, array $actions); + /** * Executes a SQL statement and returns the result as an array. * @@ -284,7 +293,7 @@ public function fetchAll($sql); /** * Inserts data into a table. * - * @param \Phinx\Db\Table $table where to insert data + * @param \Phinx\Db\Table\Table $table Table where to insert data * @param array $row * @return void */ @@ -293,7 +302,7 @@ public function insert(Table $table, $row); /** * Inserts data into a table in a bulk. * - * @param \Phinx\Db\Table $table where to insert data + * @param \Phinx\Db\Table\Table $table Table where to insert data * @param array $rows * @return void */ @@ -326,27 +335,12 @@ public function hasTable($tableName); /** * Creates the specified database table. * - * @param \Phinx\Db\Table $table Table + * @param \Phinx\Db\Table\Table $table Table + * @param \Phinx\Db\Table\Column[] $columns List of columns in the table + * @param \Phinx\Db\Table\Index[] $indexes List of indexes for the table * @return void */ - public function createTable(Table $table); - - /** - * Renames the specified database table. - * - * @param string $tableName Table Name - * @param string $newName New Name - * @return void - */ - public function renameTable($tableName, $newName); - - /** - * Drops the specified database table. - * - * @param string $tableName Table Name - * @return void - */ - public function dropTable($tableName); + public function createTable(Table $table, array $columns = [], array $indexes = []); /** * Truncates the specified table @@ -373,44 +367,6 @@ public function getColumns($tableName); */ public function hasColumn($tableName, $columnName); - /** - * Adds the specified column to a database table. - * - * @param \Phinx\Db\Table $table Table - * @param \Phinx\Db\Table\Column $column Column - * @return void - */ - public function addColumn(Table $table, Column $column); - - /** - * Renames the specified column. - * - * @param string $tableName Table Name - * @param string $columnName Column Name - * @param string $newColumnName New Column Name - * @return void - */ - public function renameColumn($tableName, $columnName, $newColumnName); - - /** - * Change a table column type. - * - * @param string $tableName Table Name - * @param string $columnName Column Name - * @param \Phinx\Db\Table\Column $newColumn New Column - * @return \Phinx\Db\Table - */ - public function changeColumn($tableName, $columnName, Column $newColumn); - - /** - * Drops the specified column. - * - * @param string $tableName Table Name - * @param string $columnName Column Name - * @return void - */ - public function dropColumn($tableName, $columnName); - /** * Checks to see if an index exists. * @@ -429,33 +385,6 @@ public function hasIndex($tableName, $columns); */ public function hasIndexByName($tableName, $indexName); - /** - * Adds the specified index to a database table. - * - * @param \Phinx\Db\Table $table Table - * @param \Phinx\Db\Table\Index $index Index - * @return void - */ - public function addIndex(Table $table, Index $index); - - /** - * Drops the specified index from a database table. - * - * @param string $tableName - * @param mixed $columns Column(s) - * @return void - */ - public function dropIndex($tableName, $columns); - - /** - * Drops the index specified by name from a database table. - * - * @param string $tableName - * @param string $indexName - * @return void - */ - public function dropIndexByName($tableName, $indexName); - /** * Checks to see if a foreign key exists. * @@ -466,25 +395,6 @@ public function dropIndexByName($tableName, $indexName); */ public function hasForeignKey($tableName, $columns, $constraint = null); - /** - * Adds the specified foreign key to a database table. - * - * @param \Phinx\Db\Table $table - * @param \Phinx\Db\Table\ForeignKey $foreignKey - * @return void - */ - public function addForeignKey(Table $table, ForeignKey $foreignKey); - - /** - * Drops the specified foreign key from a database table. - * - * @param string $tableName - * @param string[] $columns Column(s) - * @param string $constraint Constraint name - * @return void - */ - public function dropForeignKey($tableName, $columns, $constraint = null); - /** * Returns an array of the supported Phinx column types. * diff --git a/src/Phinx/Db/Adapter/AdapterWrapper.php b/src/Phinx/Db/Adapter/AdapterWrapper.php index 2e9a55d6a..2735f400e 100644 --- a/src/Phinx/Db/Adapter/AdapterWrapper.php +++ b/src/Phinx/Db/Adapter/AdapterWrapper.php @@ -28,10 +28,10 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; use Phinx\Migration\MigrationInterface; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; @@ -346,33 +346,9 @@ public function hasTable($tableName) /** * {@inheritdoc} */ - public function createTable(Table $table) + public function createTable(Table $table, array $columns = [], array $indexes = []) { - $this->getAdapter()->createTable($table); - } - - /** - * {@inheritdoc} - */ - public function renameTable($tableName, $newTableName) - { - $this->getAdapter()->renameTable($tableName, $newTableName); - } - - /** - * {@inheritdoc} - */ - public function dropTable($tableName) - { - $this->getAdapter()->dropTable($tableName); - } - - /** - * {@inheritdoc} - */ - public function truncateTable($tableName) - { - $this->getAdapter()->truncateTable($tableName); + $this->getAdapter()->createTable($table, $columns, $indexes); } /** @@ -391,38 +367,6 @@ public function hasColumn($tableName, $columnName) return $this->getAdapter()->hasColumn($tableName, $columnName); } - /** - * {@inheritdoc} - */ - public function addColumn(Table $table, Column $column) - { - $this->getAdapter()->addColumn($table, $column); - } - - /** - * {@inheritdoc} - */ - public function renameColumn($tableName, $columnName, $newColumnName) - { - $this->getAdapter()->renameColumn($tableName, $columnName, $newColumnName); - } - - /** - * {@inheritdoc} - */ - public function changeColumn($tableName, $columnName, Column $newColumn) - { - return $this->getAdapter()->changeColumn($tableName, $columnName, $newColumn); - } - - /** - * {@inheritdoc} - */ - public function dropColumn($tableName, $columnName) - { - $this->getAdapter()->dropColumn($tableName, $columnName); - } - /** * {@inheritdoc} */ @@ -439,30 +383,6 @@ public function hasIndexByName($tableName, $indexName) return $this->getAdapter()->hasIndexByName($tableName, $indexName); } - /** - * {@inheritdoc} - */ - public function addIndex(Table $table, Index $index) - { - $this->getAdapter()->addIndex($table, $index); - } - - /** - * {@inheritdoc} - */ - public function dropIndex($tableName, $columns) - { - $this->getAdapter()->dropIndex($tableName, $columns); - } - - /** - * {@inheritdoc} - */ - public function dropIndexByName($tableName, $indexName) - { - $this->getAdapter()->dropIndexByName($tableName, $indexName); - } - /** * {@inheritdoc} */ @@ -471,22 +391,6 @@ public function hasForeignKey($tableName, $columns, $constraint = null) return $this->getAdapter()->hasForeignKey($tableName, $columns, $constraint); } - /** - * {@inheritdoc} - */ - public function addForeignKey(Table $table, ForeignKey $foreignKey) - { - $this->getAdapter()->addForeignKey($table, $foreignKey); - } - - /** - * {@inheritdoc} - */ - public function dropForeignKey($tableName, $columns, $constraint = null) - { - $this->getAdapter()->dropForeignKey($tableName, $columns, $constraint); - } - /** * {@inheritdoc} */ @@ -535,6 +439,14 @@ public function dropSchema($schemaName) $this->getAdapter()->dropSchema($schemaName); } + /** + * {@inheritdoc} + */ + public function truncateTable($tableName) + { + $this->getAdapter()->truncateTable($tableName); + } + /** * {@inheritdoc} */ @@ -550,4 +462,12 @@ public function getConnection() { return $this->getAdapter()->getConnection(); } + + /** + * {@inheritdoc} + */ + public function executeActions(Table $table, array $actions) + { + $this->getAdapter()->executeActions($table, $actions); + } } diff --git a/src/Phinx/Db/Adapter/DirectActionInterface.php b/src/Phinx/Db/Adapter/DirectActionInterface.php new file mode 100644 index 000000000..6666b3744 --- /dev/null +++ b/src/Phinx/Db/Adapter/DirectActionInterface.php @@ -0,0 +1,140 @@ +getPendingColumns(); if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { $column = new Column(); $column->setName('id') @@ -280,17 +280,10 @@ public function createTable(Table $table) } // set the indexes - $indexes = $table->getIndexes(); foreach ($indexes as $index) { $sql .= ', ' . $this->getIndexSqlDefinition($index); } - // set the foreign keys - $foreignKeys = $table->getForeignKeys(); - foreach ($foreignKeys as $foreignKey) { - $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey); - } - $sql .= ') ' . $optionsStr; $sql = rtrim($sql) . ';'; @@ -301,17 +294,25 @@ public function createTable(Table $table) /** * {@inheritdoc} */ - public function renameTable($tableName, $newTableName) + protected function getRenameTableInstructions($tableName, $newTableName) { - $this->execute(sprintf('RENAME TABLE %s TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName))); + $sql = sprintf( + 'RENAME TABLE %s TO %s', + $this->quoteTableName($tableName), + $this->quoteTableName($newTableName) + ); + + return new AlterInstructions([], [$sql]); } /** * {@inheritdoc} */ - public function dropTable($tableName) + protected function getDropTableInstructions($tableName) { - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName))); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); } /** @@ -377,26 +378,25 @@ public function hasColumn($tableName, $columnName) /** * {@inheritdoc} */ - public function addColumn(Table $table, Column $column) + protected function getAddColumnInstructions(Table $table, Column $column) { - $sql = sprintf( - 'ALTER TABLE %s ADD %s %s', - $this->quoteTableName($table->getName()), + $alter = sprintf( + 'ADD %s %s', $this->quoteColumnName($column->getName()), $this->getColumnSqlDefinition($column) ); if ($column->getAfter()) { - $sql .= ' AFTER ' . $this->quoteColumnName($column->getAfter()); + $alter .= ' AFTER ' . $this->quoteColumnName($column->getAfter()); } - $this->execute($sql); + return new AlterInstructions([$alter]); } /** * {@inheritdoc} */ - public function renameColumn($tableName, $columnName, $newColumnName) + protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName) { $rows = $this->fetchAll(sprintf('DESCRIBE %s', $this->quoteTableName($tableName))); foreach ($rows as $row) { @@ -408,17 +408,14 @@ public function renameColumn($tableName, $columnName, $newColumnName) } $definition = $row['Type'] . ' ' . $null . $extra; - $this->execute( - sprintf( - 'ALTER TABLE %s CHANGE COLUMN %s %s %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName), - $this->quoteColumnName($newColumnName), - $definition - ) + $alter = sprintf( + 'CHANGE COLUMN %s %s %s', + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumnName), + $definition ); - return; + return new AlterInstructions([$alter]); } } @@ -431,33 +428,28 @@ public function renameColumn($tableName, $columnName, $newColumnName) /** * {@inheritdoc} */ - public function changeColumn($tableName, $columnName, Column $newColumn) + protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn) { $after = $newColumn->getAfter() ? ' AFTER ' . $this->quoteColumnName($newColumn->getAfter()) : ''; - $this->execute( - sprintf( - 'ALTER TABLE %s CHANGE %s %s %s%s', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName), - $this->quoteColumnName($newColumn->getName()), - $this->getColumnSqlDefinition($newColumn), - $after - ) + $alter = sprintf( + 'CHANGE %s %s %s%s', + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumn->getName()), + $this->getColumnSqlDefinition($newColumn), + $after ); + + return new AlterInstructions([$alter]); } /** * {@inheritdoc} */ - public function dropColumn($tableName, $columnName) + protected function getDropColumnInstructions($tableName, $columnName) { - $this->execute( - sprintf( - 'ALTER TABLE %s DROP COLUMN %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName) - ) - ); + $alter = sprintf('DROP COLUMN %s', $this->quoteColumnName($columnName)); + + return new AlterInstructions([$alter]); } /** @@ -520,21 +512,20 @@ public function hasIndexByName($tableName, $indexName) /** * {@inheritdoc} */ - public function addIndex(Table $table, Index $index) + protected function getAddIndexInstructions(Table $table, Index $index) { - $this->execute( - sprintf( - 'ALTER TABLE %s ADD %s', - $this->quoteTableName($table->getName()), - $this->getIndexSqlDefinition($index) - ) + $alter = sprintf( + 'ADD %s', + $this->getIndexSqlDefinition($index) ); + + return new AlterInstructions([$alter]); } /** * {@inheritdoc} */ - public function dropIndex($tableName, $columns) + protected function getDropIndexByColumnsInstructions($tableName, $columns) { if (is_string($columns)) { $columns = [$columns]; // str to array @@ -545,40 +536,40 @@ public function dropIndex($tableName, $columns) foreach ($indexes as $indexName => $index) { if ($columns == $index['columns']) { - $this->execute( - sprintf( - 'ALTER TABLE %s DROP INDEX %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($indexName) - ) - ); - - return; + return new AlterInstructions([sprintf( + 'DROP INDEX %s', + $this->quoteColumnName($indexName) + )]); } } + + throw new \InvalidArgumentException(sprintf( + "The specified index on columns '%s' does not exist", + implode(',', $columns) + )); } /** * {@inheritdoc} */ - public function dropIndexByName($tableName, $indexName) + protected function getDropIndexByNameInstructions($tableName, $indexName) { + $indexes = $this->getIndexes($tableName); foreach ($indexes as $name => $index) { - //$a = array_diff($columns, $index['columns']); if ($name === $indexName) { - $this->execute( - sprintf( - 'ALTER TABLE %s DROP INDEX %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($indexName) - ) - ); - - return; + return new AlterInstructions([sprintf( + 'DROP INDEX %s', + $this->quoteColumnName($indexName) + )]); } } + + throw new \InvalidArgumentException(sprintf( + "The specified index name '%s' does not exist", + $indexName + )); } /** @@ -643,55 +634,63 @@ protected function getForeignKeys($tableName) /** * {@inheritdoc} */ - public function addForeignKey(Table $table, ForeignKey $foreignKey) + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey) { - $this->execute( - sprintf( - 'ALTER TABLE %s ADD %s', - $this->quoteTableName($table->getName()), - $this->getForeignKeySqlDefinition($foreignKey) - ) + $alter = sprintf( + 'ADD %s', + $this->getForeignKeySqlDefinition($foreignKey) ); + + return new AlterInstructions([$alter]); } /** * {@inheritdoc} */ - public function dropForeignKey($tableName, $columns, $constraint = null) + protected function getDropForeignKeyInstructions($tableName, $constraint) { - if (is_string($columns)) { - $columns = [$columns]; // str to array - } + $alter = sprintf( + 'DROP FOREIGN KEY %s', + $constraint + ); - if ($constraint) { - $this->execute( - sprintf( - 'ALTER TABLE %s DROP FOREIGN KEY %s', - $this->quoteTableName($tableName), - $constraint - ) - ); - - return; - } else { - foreach ($columns as $column) { - $rows = $this->fetchAll(sprintf( - "SELECT - CONSTRAINT_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE REFERENCED_TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - AND TABLE_NAME = '%s' - AND COLUMN_NAME = '%s' - ORDER BY POSITION_IN_UNIQUE_CONSTRAINT", - $tableName, - $column - )); - foreach ($rows as $row) { - $this->dropForeignKey($tableName, $columns, $row['CONSTRAINT_NAME']); - } + return new AlterInstructions([$alter]); + } + + /** + * {@inheritdoc} + */ + protected function getDropForeignKeyByColumnsInstructions($tableName, $columns) + { + $instructions = new AlterInstructions(); + + foreach ($columns as $column) { + $rows = $this->fetchAll(sprintf( + "SELECT + CONSTRAINT_NAME + FROM information_schema.KEY_COLUMN_USAGE + WHERE REFERENCED_TABLE_SCHEMA = DATABASE() + AND REFERENCED_TABLE_NAME IS NOT NULL + AND TABLE_NAME = '%s' + AND COLUMN_NAME = '%s' + ORDER BY POSITION_IN_UNIQUE_CONSTRAINT", + $tableName, + $column + )); + + foreach ($rows as $row) { + $instructions->merge($this->getDropForeignKeyInstructions($tableName, $row['CONSTRAINT_NAME'])); } } + + if (empty($instructions->getAlterParts())) { + throw new \InvalidArgumentException(sprintf( + "Not foreign key on columns '%s' exist", + implode(',', $columns) + )); + } + + return $instructions; } /** diff --git a/src/Phinx/Db/Adapter/PdoAdapter.php b/src/Phinx/Db/Adapter/PdoAdapter.php index efb0725ec..21c0fdcd0 100644 --- a/src/Phinx/Db/Adapter/PdoAdapter.php +++ b/src/Phinx/Db/Adapter/PdoAdapter.php @@ -29,21 +29,52 @@ namespace Phinx\Db\Adapter; use BadMethodCallException; -use Phinx\Db\Table; +use Phinx\Db\Action\AddColumn; +use Phinx\Db\Action\AddForeignKey; +use Phinx\Db\Action\AddIndex; +use Phinx\Db\Action\ChangeColumn; +use Phinx\Db\Action\DropForeignKey; +use Phinx\Db\Action\DropIndex; +use Phinx\Db\Action\DropTable; +use Phinx\Db\Action\RemoveColumn; +use Phinx\Db\Action\RenameColumn; +use Phinx\Db\Action\RenameTable; +use Phinx\Db\Table\Column; +use Phinx\Db\Table\ForeignKey; +use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; +use Phinx\Db\Util\AlterInstructions; use Phinx\Migration\MigrationInterface; +use Symfony\Component\Console\Output\OutputInterface; /** * Phinx PDO Adapter. * * @author Rob Morgan */ -abstract class PdoAdapter extends AbstractAdapter +abstract class PdoAdapter extends AbstractAdapter implements DirectActionInterface { /** * @var \PDO|null */ protected $connection; + /** + * Writes a message to stdout if vebose output is on + * + * @param string $message The message to show + * @return void + */ + protected function verboseLog($message) + { + if (!$this->isDryRunEnabled() && + $this->getOutput()->getVerbosity() < OutputInterface::VERBOSITY_VERY_VERBOSE) { + return; + } + + $this->getOutput()->writeln($message); + } + /** * {@inheritdoc} */ @@ -72,7 +103,7 @@ public function setConnection(\PDO $connection) if (!$this->hasSchemaTable()) { $this->createSchemaTable(); } else { - $table = new Table($this->getSchemaTableName(), [], $this); + $table = new \Phinx\Db\Table($this->getSchemaTableName(), [], $this); if (!$table->hasColumn('migration_name')) { $table ->addColumn( @@ -125,9 +156,9 @@ public function disconnect() */ public function execute($sql) { - if ($this->isDryRunEnabled()) { - $this->getOutput()->writeln($sql); + $this->verboseLog($sql); + if ($this->isDryRunEnabled()) { return 0; } @@ -445,4 +476,326 @@ protected function getDefaultValueDefinition($default, $columnType = null) return isset($default) ? " DEFAULT $default" : ''; } + + /** + * Executes all the ALTER TABLE instructions passed for the given table + * + * @param string $tableName The table name to use in the ALTER statement + * @param AlterInstructions $instructions The object containing the alter sequence + * @return void + */ + protected function executeAlterSteps($tableName, AlterInstructions $instructions) + { + $alter = sprintf('ALTER TABLE %s %%s', $this->quoteTableName($tableName)); + $instructions->execute($alter, [$this, 'execute']); + } + + /** + * {@inheritdoc} + */ + public function addColumn(Table $table, Column $column) + { + $instructions = $this->getAddColumnInstructions($table, $column); + $this->executeAlterSteps($table, $instructions); + } + + /** + * Returns the instrutions to add the specified column to a database table. + * + * @param \Phinx\Db\Table\Table $table Table + * @param \Phinx\Db\Table\Column $column Column + * @return AlterInstructions + */ + abstract protected function getAddColumnInstructions(Table $table, Column $column); + + /** + * {@inheritdoc} + */ + public function renameColumn($tableName, $columnName, $newColumnName) + { + $instructions = $this->getRenameColumnInstructions($tableName, $columnName, $newColumnName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to rename the specified column. + * + * @param string $tableName Table Name + * @param string $columnName Column Name + * @param string $newColumnName New Column Name + * @return AlterInstructions:w + * + */ + abstract protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName); + + /** + * {@inheritdoc} + */ + public function changeColumn($tableName, $columnName, Column $newColumn) + { + $instructions = $this->getChangeColumnInstructions($tableName, $columnName, $newColumn); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to change a table column type. + * + * @param string $tableName Table Name + * @param string $columnName Column Name + * @param \Phinx\Db\Table\Column $newColumn New Column + * @return AlterInstructions + */ + abstract protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn); + + /** + * {@inheritdoc} + */ + public function dropColumn($tableName, $columnName) + { + $instructions = $this->getDropColumnInstructions($tableName, $columnName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified column. + * + * @param string $tableName Table Name + * @param string $columnName Column Name + * @return AlterInstructions + */ + abstract protected function getDropColumnInstructions($tableName, $columnName); + + /** + * {@inheritdoc} + */ + public function addIndex(Table $table, Index $index) + { + $instructions = $this->getAddIndexInstructions($table, $index); + $this->executeAlterSteps($table->getName(), $instructions); + } + + /** + * Returns the instructions to add the specified index to a database table. + * + * @param \Phinx\Db\Table\Table $table Table + * @param \Phinx\Db\Table\Index $index Index + * @return AlterInstructions + */ + abstract protected function getAddIndexInstructions(Table $table, Index $index); + + /** + * {@inheritdoc} + */ + public function dropIndex($tableName, $columns) + { + $instructions = $this->getDropIndexByColumnsInstructions($tableName, $columns); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified index from a database table. + * + * @param string $tableName The name of of the table where the index is + * @param mixed $columns Column(s) + * @return AlterInstructions + */ + abstract protected function getDropIndexByColumnsInstructions($tableName, $columns); + + /** + * {@inheritdoc} + */ + public function dropIndexByName($tableName, $indexName) + { + $instructions = $this->getDropIndexByNameInstructions($tableName, $indexName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the index specified by name from a database table. + * + * @param string $tableName The table name whe the index is + * @param string $indexName The name of the index + * @return AlterInstructions + */ + abstract protected function getDropIndexByNameInstructions($tableName, $indexName); + + /** + * {@inheritdoc} + */ + public function addForeignKey(Table $table, ForeignKey $foreignKey) + { + $instructions = $this->getAddForeignKeyInstructions($table, $foreignKey); + $this->executeAlterSteps($table->getName(), $instructions); + } + + /** + * Returns the instructions to adds the specified foreign key to a database table. + * + * @param \Phinx\Db\Table\Table $table The table to add the constraint to + * @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add + * @return AlterInstructions + */ + abstract protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey); + + /** + * {@inheritdoc} + */ + public function dropForeignKey($tableName, $columns, $constraint = null) + { + if ($constraint) { + $instructions = $this->getDropForeignKeyInstructions($tableName, $constraint); + } else { + $instructions = $this->getDropForeignKeyByColumnsInstructions($tableName, $columns); + } + + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified foreign key from a database table. + * + * @param string $tableName The table where the foreign key constraint is + * @param string $constraint Constraint name + * @return AlterInstructions + */ + abstract protected function getDropForeignKeyInstructions($tableName, $constraint); + + /** + * Returns the instructions to drop the specified foreign key from a database table. + * + * @param string $tableName The table where the foreign key constraint is + * @param array $columns The list of column names + * @return AlterInstructions + */ + abstract protected function getDropForeignKeyByColumnsInstructions($tableName, $columns); + + /** + * {@inheritdoc} + */ + public function dropTable($tableName) + { + $instructions = $this->getDropTableInstructions($tableName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to drop the specified database table. + * + * @param string $tableName Table Name + * @return AlterInstructions + */ + abstract protected function getDropTableInstructions($tableName); + + /** + * {@inheritdoc} + */ + public function renameTable($tableName, $newTableName) + { + $instructions = $this->getRenameTableInstructions($tableName, $newTableName); + $this->executeAlterSteps($tableName, $instructions); + } + + /** + * Returns the instructions to rename the specified database table. + * + * @param string $tableName Table Name + * @param string $newTableName New Name + * @return AlterInstructions + */ + abstract protected function getRenameTableInstructions($tableName, $newTableName); + + /** + * {@inheritdoc} + */ + public function executeActions(Table $table, array $actions) + { + $instructions = new AlterInstructions(); + + foreach ($actions as $action) { + switch (true) { + case ($action instanceof AddColumn): + $instructions->merge($this->getAddColumnInstructions($table, $action->getColumn())); + break; + + case ($action instanceof AddIndex): + $instructions->merge($this->getAddIndexInstructions($table, $action->getIndex())); + break; + + case ($action instanceof AddForeignKey): + $instructions->merge($this->getAddForeignKeyInstructions($table, $action->getForeignKey())); + break; + + case ($action instanceof ChangeColumn): + $instructions->merge($this->getChangeColumnInstructions( + $table->getName(), + $action->getColumnName(), + $action->getColumn() + )); + break; + + case ($action instanceof DropForeignKey && !$action->getForeignKey()->getConstraint()): + $instructions->merge($this->getDropForeignKeyByColumnsInstructions( + $table->getName(), + $action->getForeignKey()->getColumns() + )); + break; + + case ($action instanceof DropForeignKey && $action->getForeignKey()->getConstraint()): + $instructions->merge($this->getDropForeignKeyInstructions( + $table->getName(), + $action->getForeignKey()->getConstraint() + )); + break; + + case ($action instanceof DropIndex && $action->getIndex()->getName() !== null): + $instructions->merge($this->getDropIndexByNameInstructions( + $table->getName(), + $action->getIndex()->getName() + )); + break; + + case ($action instanceof DropIndex && $action->getIndex()->getName() == null): + $instructions->merge($this->getDropIndexByColumnsInstructions( + $table->getName(), + $action->getIndex()->getColumns() + )); + break; + + case ($action instanceof DropTable): + $instructions->merge($this->getDropTableInstructions( + $table->getName() + )); + break; + + case ($action instanceof RemoveColumn): + $instructions->merge($this->getDropColumnInstructions( + $table->getName(), + $action->getColumn()->getName() + )); + break; + + case ($action instanceof RenameColumn): + $instructions->merge($this->getRenameColumnInstructions( + $table->getName(), + $action->getColumn()->getName(), + $action->getNewName() + )); + break; + + case ($action instanceof RenameTable): + $instructions->merge($this->getRenameTableInstructions( + $table->getName(), + $action->getNewName() + )); + break; + + default: + throw new \InvalidArgumentException( + sprintf("Don't know how to execute action: '%s'", get_class($action)) + ); + } + } + + $this->executeAlterSteps($table->getName(), $instructions); + } } diff --git a/src/Phinx/Db/Adapter/PostgresAdapter.php b/src/Phinx/Db/Adapter/PostgresAdapter.php index 54ffa16a6..e86dbec24 100644 --- a/src/Phinx/Db/Adapter/PostgresAdapter.php +++ b/src/Phinx/Db/Adapter/PostgresAdapter.php @@ -28,10 +28,11 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; +use Phinx\Db\Util\AlterInstructions; use Phinx\Util\Literal; class PostgresAdapter extends PdoAdapter implements AdapterInterface @@ -181,12 +182,11 @@ public function hasTable($tableName) /** * {@inheritdoc} */ - public function createTable(Table $table) + public function createTable(Table $table, array $columns = [], array $indexes = []) { $options = $table->getOptions(); // Add the default primary key - $columns = $table->getPendingColumns(); if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { $column = new Column(); $column->setName('id') @@ -234,14 +234,6 @@ public function createTable(Table $table) $sql = rtrim($sql, ', '); // no primary keys } - // set the foreign keys - $foreignKeys = $table->getForeignKeys(); - if (!empty($foreignKeys)) { - foreach ($foreignKeys as $foreignKey) { - $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey, $table->getName()); - } - } - $sql .= ');'; // process column comments @@ -252,7 +244,6 @@ public function createTable(Table $table) } // set the indexes - $indexes = $table->getIndexes(); if (!empty($indexes)) { foreach ($indexes as $index) { $sql .= $this->getIndexSqlDefinition($index, $table->getName()); @@ -276,22 +267,25 @@ public function createTable(Table $table) /** * {@inheritdoc} */ - public function renameTable($tableName, $newTableName) + protected function getRenameTableInstructions($tableName, $newTableName) { $sql = sprintf( 'ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $this->quoteColumnName($newTableName) ); - $this->execute($sql); + + return new AlterInstructions([], [$sql]); } /** * {@inheritdoc} */ - public function dropTable($tableName) + protected function getDropTableInstructions($tableName) { - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName))); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); } /** @@ -396,26 +390,26 @@ public function hasColumn($tableName, $columnName) /** * {@inheritdoc} */ - public function addColumn(Table $table, Column $column) + protected function getAddColumnInstructions(Table $table, Column $column) { - $sql = sprintf( - 'ALTER TABLE %s ADD %s %s;', - $this->quoteTableName($table->getName()), + $instructions = new AlterInstructions(); + $instructions->addAlter(sprintf( + 'ADD %s %s', $this->quoteColumnName($column->getName()), $this->getColumnSqlDefinition($column) - ); + )); if ($column->getComment()) { - $sql .= $this->getColumnCommentSqlDefinition($column, $table->getName()); + $instructions->addPostStep($this->getColumnCommentSqlDefinition($column, $table->getName())); } - $this->execute($sql); + return $instructions; } /** * {@inheritdoc} */ - public function renameColumn($tableName, $columnName, $newColumnName) + protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName) { $sql = sprintf( "SELECT CASE WHEN COUNT(*) > 0 THEN 1 ELSE 0 END AS column_exists @@ -424,102 +418,103 @@ public function renameColumn($tableName, $columnName, $newColumnName) $tableName, $columnName ); + $result = $this->fetchRow($sql); if (!(bool)$result['column_exists']) { throw new \InvalidArgumentException("The specified column does not exist: $columnName"); } - $this->execute( + + $instructions = new AlterInstructions(); + $instructions->addPostStep( sprintf( 'ALTER TABLE %s RENAME COLUMN %s TO %s', - $this->quoteTableName($tableName), + $tableName, $this->quoteColumnName($columnName), $this->quoteColumnName($newColumnName) ) ); + + return $instructions; } /** * {@inheritdoc} */ - public function changeColumn($tableName, $columnName, Column $newColumn) + protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn) { - // TODO - is it possible to merge these 3 queries into less? - // change data type + $instructions = new AlterInstructions(); + $sql = sprintf( - 'ALTER TABLE %s ALTER COLUMN %s TYPE %s', - $this->quoteTableName($tableName), + 'ALTER COLUMN %s TYPE %s', $this->quoteColumnName($columnName), $this->getColumnSqlDefinition($newColumn) ); + // //NULL and DEFAULT cannot be set while changing column type $sql = preg_replace('/ NOT NULL/', '', $sql); $sql = preg_replace('/ NULL/', '', $sql); //If it is set, DEFAULT is the last definition $sql = preg_replace('/DEFAULT .*/', '', $sql); - $this->execute($sql); + + $instructions->addAlter($sql); + // process null $sql = sprintf( - 'ALTER TABLE %s ALTER COLUMN %s', - $this->quoteTableName($tableName), + 'ALTER COLUMN %s', $this->quoteColumnName($columnName) ); + if ($newColumn->isNull()) { $sql .= ' DROP NOT NULL'; } else { $sql .= ' SET NOT NULL'; } - $this->execute($sql); + + $instructions->addAlter($sql); + if (!is_null($newColumn->getDefault())) { - //change default - $this->execute( - sprintf( - 'ALTER TABLE %s ALTER COLUMN %s SET %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName), - $this->getDefaultValueDefinition($newColumn->getDefault(), $newColumn->getType()) - ) - ); + $instructions->addAlter(sprintf( + 'ALTER COLUMN %s SET %s', + $this->quoteColumnName($columnName), + $this->getDefaultValueDefinition($newColumn->getDefault(), $newColumn->getType()) + )); } else { //drop default - $this->execute( - sprintf( - 'ALTER TABLE %s ALTER COLUMN %s DROP DEFAULT', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName) - ) - ); + $instructions->addAlter(sprintf( + 'ALTER COLUMN %s DROP DEFAULT', + $this->quoteColumnName($columnName) + )); } + // rename column if ($columnName !== $newColumn->getName()) { - $this->execute( - sprintf( - 'ALTER TABLE %s RENAME COLUMN %s TO %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName), - $this->quoteColumnName($newColumn->getName()) - ) - ); + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s RENAME COLUMN %s TO %s', + $this->quoteTableName($tableName), + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumn->getName()) + )); } // change column comment if needed if ($newColumn->getComment()) { - $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName); - $this->execute($sql); + $instructions->addPostStep($this->getColumnCommentSqlDefinition($newColumn, $tableName)); } + + return $instructions; } /** * {@inheritdoc} */ - public function dropColumn($tableName, $columnName) + protected function getDropColumnInstructions($tableName, $columnName) { - $this->execute( - sprintf( - 'ALTER TABLE %s DROP COLUMN %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName) - ) + $alter = sprintf( + 'DROP COLUMN %s', + $this->quoteColumnName($columnName) ); + + return new AlterInstructions([$alter]); } /** @@ -597,16 +592,18 @@ public function hasIndexByName($tableName, $indexName) /** * {@inheritdoc} */ - public function addIndex(Table $table, Index $index) + protected function getAddIndexInstructions(Table $table, Index $index) { - $sql = $this->getIndexSqlDefinition($index, $table->getName()); - $this->execute($sql); + $instructions = new AlterInstructions(); + $instructions->addPostStep($this->getIndexSqlDefinition($index, $table->getName())); + + return $instructions; } /** * {@inheritdoc} */ - public function dropIndex($tableName, $columns) + protected function getDropIndexByColumnsInstructions($tableName, $columns) { if (is_string($columns)) { $columns = [$columns]; // str to array @@ -618,28 +615,30 @@ public function dropIndex($tableName, $columns) foreach ($indexes as $indexName => $index) { $a = array_diff($columns, $index['columns']); if (empty($a)) { - $this->execute( - sprintf( - 'DROP INDEX IF EXISTS %s', - $this->quoteColumnName($indexName) - ) - ); - - return; + return new AlterInstructions([], [sprintf( + 'DROP INDEX IF EXISTS %s', + $this->quoteColumnName($indexName) + )]); } } + + throw new \InvalidArgumentException(sprintf( + "The specified index on columns '%s' does not exist", + implode(',', $columns) + )); } /** * {@inheritdoc} */ - public function dropIndexByName($tableName, $indexName) + protected function getDropIndexByNameInstructions($tableName, $indexName) { $sql = sprintf( 'DROP INDEX IF EXISTS %s', - $indexName + $this->quoteColumnName($indexName) ); - $this->execute($sql); + + return new AlterInstructions([], [$sql]); } /** @@ -705,52 +704,56 @@ protected function getForeignKeys($tableName) /** * {@inheritdoc} */ - public function addForeignKey(Table $table, ForeignKey $foreignKey) + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey) { - $sql = sprintf( - 'ALTER TABLE %s ADD %s', - $this->quoteTableName($table->getName()), + $alter = sprintf( + 'ADD %s', $this->getForeignKeySqlDefinition($foreignKey, $table->getName()) ); - $this->execute($sql); + + return new AlterInstructions([$alter]); } /** * {@inheritdoc} */ - public function dropForeignKey($tableName, $columns, $constraint = null) + protected function getDropForeignKeyInstructions($tableName, $constraint) { - if (is_string($columns)) { - $columns = [$columns]; // str to array - } + $alter = sprintf( + 'DROP CONSTRAINT %s', + $constraint + ); - if ($constraint) { - $this->execute( - sprintf( - 'ALTER TABLE %s DROP CONSTRAINT %s', - $this->quoteTableName($tableName), - $constraint - ) - ); - } else { - foreach ($columns as $column) { - $rows = $this->fetchAll(sprintf( - "SELECT CONSTRAINT_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE TABLE_SCHEMA = CURRENT_SCHEMA() - AND TABLE_NAME IS NOT NULL - AND TABLE_NAME = '%s' - AND COLUMN_NAME = '%s' - ORDER BY POSITION_IN_UNIQUE_CONSTRAINT", - $tableName, - $column - )); - - foreach ($rows as $row) { - $this->dropForeignKey($tableName, $columns, $row['constraint_name']); - } + return new AlterInstructions([$alter]); + } + + /** + * {@inheritdoc} + */ + protected function getDropForeignKeyByColumnsInstructions($tableName, $columns) + { + $instructions = new AlterInstructions(); + + foreach ($columns as $column) { + $rows = $this->fetchAll(sprintf( + "SELECT CONSTRAINT_NAME + FROM information_schema.KEY_COLUMN_USAGE + WHERE TABLE_SCHEMA = CURRENT_SCHEMA() + AND TABLE_NAME IS NOT NULL + AND TABLE_NAME = '%s' + AND COLUMN_NAME = '%s' + ORDER BY POSITION_IN_UNIQUE_CONSTRAINT", + $tableName, + $column + )); + + foreach ($rows as $row) { + $newInstr = $this->getDropForeignKeyInstructions($tableName, $row['constraint_name']); + $instructions->merge($newInstr); } } + + return $instructions; } /** diff --git a/src/Phinx/Db/Adapter/ProxyAdapter.php b/src/Phinx/Db/Adapter/ProxyAdapter.php index d31c7c760..d9ebee1fd 100644 --- a/src/Phinx/Db/Adapter/ProxyAdapter.php +++ b/src/Phinx/Db/Adapter/ProxyAdapter.php @@ -28,10 +28,19 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; -use Phinx\Db\Table\Column; -use Phinx\Db\Table\ForeignKey; -use Phinx\Db\Table\Index; +use Phinx\Db\Action\AddColumn; +use Phinx\Db\Action\AddForeignKey; +use Phinx\Db\Action\AddIndex; +use Phinx\Db\Action\CreateTable; +use Phinx\Db\Action\DropForeignKey; +use Phinx\Db\Action\DropIndex; +use Phinx\Db\Action\DropTable; +use Phinx\Db\Action\RemoveColumn; +use Phinx\Db\Action\RenameColumn; +use Phinx\Db\Action\RenameTable; +use Phinx\Db\Plan\Intent; +use Phinx\Db\Plan\Plan; +use Phinx\Db\Table\Table; use Phinx\Migration\IrreversibleMigrationException; /** @@ -46,7 +55,7 @@ class ProxyAdapter extends AdapterWrapper /** * @var array */ - protected $commands; + protected $commands = []; /** * {@inheritdoc} @@ -59,199 +68,67 @@ public function getAdapterType() /** * {@inheritdoc} */ - public function createTable(Table $table) + public function createTable(Table $table, array $columns = [], array $indexes = []) { - $this->recordCommand('createTable', [$table->getName()]); + $this->commands[] = new CreateTable($table); } /** * {@inheritdoc} */ - public function renameTable($tableName, $newTableName) + public function executeActions(Table $table, array $actions) { - $this->recordCommand('renameTable', [$tableName, $newTableName]); - } - - /** - * {@inheritdoc} - */ - public function dropTable($tableName) - { - $this->recordCommand('dropTable', [$tableName]); - } - - /** - * {@inheritdoc} - */ - public function truncateTable($tableName) - { - $this->recordCommand('truncateTable', [$tableName]); - } - - /** - * {@inheritdoc} - */ - public function addColumn(Table $table, Column $column) - { - $this->recordCommand('addColumn', [$table, $column]); - } - - /** - * {@inheritdoc} - */ - public function renameColumn($tableName, $columnName, $newColumnName) - { - $this->recordCommand('renameColumn', [$tableName, $columnName, $newColumnName]); - } - - /** - * {@inheritdoc} - */ - public function changeColumn($tableName, $columnName, Column $newColumn) - { - $this->recordCommand('changeColumn', [$tableName, $columnName, $newColumn]); - } - - /** - * {@inheritdoc} - */ - public function dropColumn($tableName, $columnName) - { - $this->recordCommand('dropColumn', [$tableName, $columnName]); - } - - /** - * {@inheritdoc} - */ - public function addIndex(Table $table, Index $index) - { - $this->recordCommand('addIndex', [$table, $index]); - } - - /** - * {@inheritdoc} - */ - public function dropIndex($tableName, $columns, $options = []) - { - $this->recordCommand('dropIndex', [$tableName, $columns, $options]); - } - - /** - * {@inheritdoc} - */ - public function dropIndexByName($tableName, $indexName) - { - $this->recordCommand('dropIndexByName', [$tableName, $indexName]); - } - - /** - * {@inheritdoc} - */ - public function addForeignKey(Table $table, ForeignKey $foreignKey) - { - $this->recordCommand('addForeignKey', [$table, $foreignKey]); - } - - /** - * {@inheritdoc} - */ - public function dropForeignKey($tableName, $columns, $constraint = null) - { - $this->recordCommand('dropForeignKey', [$columns, $constraint]); - } - - /** - * {@inheritdoc} - */ - public function createDatabase($name, $options = []) - { - $this->recordCommand('createDatabase', [$name, $options]); - } - - /** - * Record a command for execution later. - * - * @param string $name Command Name - * @param array $arguments Command Arguments - * @return void - */ - public function recordCommand($name, $arguments) - { - $this->commands[] = [ - 'name' => $name, - 'arguments' => $arguments - ]; - } - - /** - * Sets an array of recorded commands. - * - * @param array $commands Commands - * @return \Phinx\Db\Adapter\ProxyAdapter - */ - public function setCommands($commands) - { - $this->commands = $commands; - - return $this; - } - - /** - * Gets an array of the recorded commands. - * - * @return array|null - */ - public function getCommands() - { - return $this->commands; + $this->commands = array_merge($this->commands, $actions); } /** * Gets an array of the recorded commands in reverse. * * @throws \Phinx\Migration\IrreversibleMigrationException if a command cannot be reversed. - * @return array + * @return \Phinx\Db\Plan\Intent */ public function getInvertedCommands() { - if ($this->getCommands() === null) { - return []; - } - - $invCommands = []; - $supportedCommands = [ - 'createTable', 'renameTable', 'addColumn', - 'renameColumn', 'addIndex', 'addForeignKey' - ]; - foreach (array_reverse($this->getCommands()) as $command) { - if (!in_array($command['name'], $supportedCommands)) { - throw new IrreversibleMigrationException(sprintf( - 'Cannot reverse a "%s" command', - $command['name'] - )); + $inverted = new Intent(); + + foreach (array_reverse($this->commands) as $com) { + switch (true) { + case $com instanceof CreateTable: + $inverted->addAction(new DropTable($com->getTable())); + break; + + case $com instanceof RenameTable: + $inverted->addAction(new RenameTable(new Table($com->getNewName()), $com->getTable()->getName())); + break; + + case $com instanceof AddColumn: + $inverted->addAction(new RemoveColumn($com->getTable(), $com->getColumn())); + break; + + case $com instanceof RenameColumn: + $column = clone $com->getColumn(); + $name = $column->getName(); + $column->setName($com->getNewName()); + $inverted->addAction(new RenameColumn($com->getTable(), $column, $name)); + break; + + case $com instanceof AddIndex: + $inverted->addAction(new DropIndex($com->getTable(), $com->getIndex())); + break; + + case $com instanceof AddForeignKey: + $inverted->addAction(new DropForeignKey($com->getTable(), $com->getForeignKey())); + break; + + default: + throw new IrreversibleMigrationException(sprintf( + 'Cannot reverse a "%s" command', + get_class($com) + )); } - $invertMethod = 'invert' . ucfirst($command['name']); - $invertedCommand = $this->$invertMethod($command['arguments']); - $invCommands[] = [ - 'name' => $invertedCommand['name'], - 'arguments' => $invertedCommand['arguments'] - ]; } - return $invCommands; - } - - /** - * Execute the recorded commands. - * - * @return void - */ - public function executeCommands() - { - $commands = $this->getCommands(); - foreach ($commands as $command) { - call_user_func_array([$this->getAdapter(), $command['name']], $command['arguments']); - } + return $inverted; } /** @@ -261,75 +138,7 @@ public function executeCommands() */ public function executeInvertedCommands() { - $commands = $this->getInvertedCommands(); - foreach ($commands as $command) { - call_user_func_array([$this->getAdapter(), $command['name']], $command['arguments']); - } - } - - /** - * Returns the reverse of a createTable command. - * - * @param array $args Method Arguments - * @return array - */ - public function invertCreateTable($args) - { - return ['name' => 'dropTable', 'arguments' => [$args[0]]]; - } - - /** - * Returns the reverse of a renameTable command. - * - * @param array $args Method Arguments - * @return array - */ - public function invertRenameTable($args) - { - return ['name' => 'renameTable', 'arguments' => [$args[1], $args[0]]]; - } - - /** - * Returns the reverse of a addColumn command. - * - * @param array $args Method Arguments - * @return array - */ - public function invertAddColumn($args) - { - return ['name' => 'dropColumn', 'arguments' => [$args[0]->getName(), $args[1]->getName()]]; - } - - /** - * Returns the reverse of a renameColumn command. - * - * @param array $args Method Arguments - * @return array - */ - public function invertRenameColumn($args) - { - return ['name' => 'renameColumn', 'arguments' => [$args[0], $args[2], $args[1]]]; - } - - /** - * Returns the reverse of a addIndex command. - * - * @param array $args Method Arguments - * @return array - */ - public function invertAddIndex($args) - { - return ['name' => 'dropIndex', 'arguments' => [$args[0]->getName(), $args[1]->getColumns()]]; - } - - /** - * Returns the reverse of a addForeignKey command. - * - * @param array $args Method Arguments - * @return array - */ - public function invertAddForeignKey($args) - { - return ['name' => 'dropForeignKey', 'arguments' => [$args[0]->getName(), $args[1]->getColumns()]]; + $plan = new Plan($this->getInvertedCommands()); + $plan->executeInverse($this->getAdapter()); } } diff --git a/src/Phinx/Db/Adapter/SQLiteAdapter.php b/src/Phinx/Db/Adapter/SQLiteAdapter.php index 0b84935fb..f0c8b4bac 100644 --- a/src/Phinx/Db/Adapter/SQLiteAdapter.php +++ b/src/Phinx/Db/Adapter/SQLiteAdapter.php @@ -28,10 +28,11 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; +use Phinx\Db\Util\AlterInstructions; use Phinx\Util\Literal; /** @@ -85,6 +86,7 @@ public function connect() )); } + $db->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->setConnection($db); } } @@ -162,10 +164,9 @@ public function hasTable($tableName) /** * {@inheritdoc} */ - public function createTable(Table $table) + public function createTable(Table $table, array $columns = [], array $indexes = []) { // Add the default primary key - $columns = $table->getPendingColumns(); $options = $table->getOptions(); if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { $column = new Column(); @@ -218,19 +219,11 @@ public function createTable(Table $table) $sql = substr(rtrim($sql), 0, -1); // no primary keys } - // set the foreign keys - $foreignKeys = $table->getForeignKeys(); - if (!empty($foreignKeys)) { - foreach ($foreignKeys as $foreignKey) { - $sql .= ', ' . $this->getForeignKeySqlDefinition($foreignKey); - } - } - $sql = rtrim($sql) . ');'; // execute the sql $this->execute($sql); - foreach ($table->getIndexes() as $index) { + foreach ($indexes as $index) { $this->addIndex($table, $index); } } @@ -238,17 +231,25 @@ public function createTable(Table $table) /** * {@inheritdoc} */ - public function renameTable($tableName, $newTableName) + protected function getRenameTableInstructions($tableName, $newTableName) { - $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $this->quoteTableName($newTableName))); + $sql = sprintf( + 'ALTER TABLE %s RENAME TO %s', + $this->quoteTableName($tableName), + $this->quoteTableName($newTableName) + ); + + return new AlterInstructions([], [$sql]); } /** * {@inheritdoc} */ - public function dropTable($tableName) + protected function getDropTableInstructions($tableName) { - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName))); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); } /** @@ -311,25 +312,25 @@ public function hasColumn($tableName, $columnName) /** * {@inheritdoc} */ - public function addColumn(Table $table, Column $column) + protected function getAddColumnInstructions(Table $table, Column $column) { - $sql = sprintf( - 'ALTER TABLE %s ADD COLUMN %s %s', - $this->quoteTableName($table->getName()), + $alter = sprintf( + 'ADD COLUMN %s %s', $this->quoteColumnName($column->getName()), $this->getColumnSqlDefinition($column) ); - $this->execute($sql); + return new AlterInstructions([$alter]); } /** - * {@inheritdoc} + * Returns the original CREATE statement for the give table + * + * @param string $tableName The table name to get the create statement for + * @return string */ - public function renameColumn($tableName, $columnName, $newColumnName) + protected function getDeclaringSql($tableName) { - $tmpTableName = 'tmp_' . $tableName; - $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\''); $sql = ''; @@ -339,159 +340,215 @@ public function renameColumn($tableName, $columnName, $newColumnName) } } - $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName))); - $selectColumns = []; - $writeColumns = []; - foreach ($columns as $column) { - $selectName = $column['name']; - $writeName = ($selectName == $columnName)? $newColumnName : $selectName; - $selectColumns[] = $this->quoteColumnName($selectName); - $writeColumns[] = $this->quoteColumnName($writeName); - } - - if (!in_array($this->quoteColumnName($columnName), $selectColumns)) { - throw new \InvalidArgumentException(sprintf( - 'The specified column doesn\'t exist: ' . $columnName - )); - } - - $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName)); - - $sql = str_replace( - $this->quoteColumnName($columnName), - $this->quoteColumnName($newColumnName), - $sql - ); - $this->execute($sql); + return $sql; + } + /** + * Copies all the data from a tmp table to another table + * + * @param string $tableName The table name to copy the data to + * @param string $tmpTableName The tmp table name where the data is stored + * @param string[] $writeColumns The list of columns in the target table + * @param string[] $selectColumns The list of columns in the tmp table + * @return void + */ + protected function copyDataToNewTable($tableName, $tmpTableName, $writeColumns, $selectColumns) + { $sql = sprintf( 'INSERT INTO %s(%s) SELECT %s FROM %s', - $tableName, + $this->quoteTableName($tableName), implode(', ', $writeColumns), implode(', ', $selectColumns), - $tmpTableName + $this->quoteTableName($tmpTableName) ); - $this->execute($sql); - - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName))); } /** - * {@inheritdoc} + * Modifies the passed instructions to copy all data from the tmp table into + * the provided table and then drops the tmp table. + * + * @param AlterInstructions $instructions The instructions to modify + * @param string $tableName The table name to copy the data to + * @return AlterInstructions */ - public function changeColumn($tableName, $columnName, Column $newColumn) + protected function copyAndDropTmpTable($instructions, $tableName) { - // TODO: DRY this up.... - $tmpTableName = 'tmp_' . $tableName; + $instructions->addPostStep(function ($state) use ($tableName) { + $this->copyDataToNewTable( + $tableName, + $state['tmpTableName'], + $state['writeColumns'], + $state['selectColumns'] + ); - $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\''); + $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($state['tmpTableName']))); - $sql = ''; - foreach ($rows as $table) { - if ($table['tbl_name'] === $tableName) { - $sql = $table['sql']; - } - } + return $state; + }); + + return $instructions; + } + /** + * Returns the columns and type to use when copying a table to another in the process + * of altering a table + * + * @param string $tableName The table to modify + * @param string $columnName The column name that is about to change + * @param string|false $newColumnName Optionally the new name for the column + * @return AlterInstructions + */ + protected function calculateNewTableColumns($tableName, $columnName, $newColumnName) + { $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName))); $selectColumns = []; $writeColumns = []; + $columnType = null; + $found = false; + foreach ($columns as $column) { $selectName = $column['name']; - $writeName = ($selectName === $columnName)? $newColumn->getName() : $selectName; - $selectColumns[] = $this->quoteColumnName($selectName); - $writeColumns[] = $this->quoteColumnName($writeName); + $writeName = $selectName; + + if ($selectName == $columnName) { + $writeName = $newColumnName; + $found = true; + $columnType = $column['type']; + $selectName = $newColumnName === false ? $newColumnName : $selectName; + } + + $selectColumns[] = $selectName; + $writeColumns[] = $writeName; } - if (!in_array($this->quoteColumnName($columnName), $selectColumns)) { + $selectColumns = array_filter($selectColumns, 'strlen'); + $writeColumns = array_filter($writeColumns, 'strlen'); + $selectColumns = array_map([$this, 'quoteColumnName'], $selectColumns); + $writeColumns = array_map([$this, 'quoteColumnName'], $writeColumns); + + if (!$found) { throw new \InvalidArgumentException(sprintf( 'The specified column doesn\'t exist: ' . $columnName )); } - $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName)); + return compact('writeColumns', 'selectColumns', 'columnType'); + } - $sql = preg_replace( - sprintf("/%s(?:\/\*.*?\*\/|\([^)]+\)|'[^']*?'|[^,])+([,)])/", $this->quoteColumnName($columnName)), - sprintf('%s %s$1', $this->quoteColumnName($newColumn->getName()), $this->getColumnSqlDefinition($newColumn)), - $sql, - 1 - ); + /** + * Returns the initial instructions to alter a table using the + * rename-alter-copy strategy + * + * @param string $tableName The table to modify + * @return AlterInstructions + */ + protected function beginAlterByCopyTable($tableName) + { + $instructions = new AlterInstructions(); + $instructions->addPostStep(function ($state) use ($tableName) { + $createSQL = $this->getDeclaringSql($tableName); + + $tmpTableName = 'tmp_' . $tableName; + $this->execute( + sprintf( + 'ALTER TABLE %s RENAME TO %s', + $this->quoteTableName($tableName), + $this->quoteTableName($tmpTableName) + ) + ); - $this->execute($sql); + return compact('createSQL', 'tmpTableName') + $state; + }); - $sql = sprintf( - 'INSERT INTO %s(%s) SELECT %s FROM %s', - $tableName, - implode(', ', $writeColumns), - implode(', ', $selectColumns), - $tmpTableName - ); + return $instructions; + } - $this->execute($sql); - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName))); + /** + * {@inheritdoc} + */ + protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName) + { + $instructions = $this->beginAlterByCopyTable($tableName); + $instructions->addPostStep(function ($state) use ($columnName, $newColumnName) { + $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columnName, $newColumnName); + + return $newState + $state; + }); + + $instructions->addPostStep(function ($state) use ($columnName, $newColumnName) { + $sql = str_replace( + $this->quoteColumnName($columnName), + $this->quoteColumnName($newColumnName), + $state['createSQL'] + ); + $this->execute($sql); + + return $state; + }); + + return $this->copyAndDropTmpTable($instructions, $tableName); } /** * {@inheritdoc} */ - public function dropColumn($tableName, $columnName) + protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn) { - // TODO: DRY this up.... - $tmpTableName = 'tmp_' . $tableName; + $instructions = $this->beginAlterByCopyTable($tableName); - $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\''); + $newColumnName = $newColumn->getName(); + $instructions->addPostStep(function ($state) use ($columnName, $newColumnName) { + $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columnName, $newColumnName); - $sql = ''; - foreach ($rows as $table) { - if ($table['tbl_name'] === $tableName) { - $sql = $table['sql']; - } - } + return $newState + $state; + }); - $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName))); - $columns = []; - $columnType = null; - foreach ($rows as $row) { - if ($row['name'] !== $columnName) { - $columns[] = $row['name']; - } else { - $found = true; - $columnType = $row['type']; - } - } + $instructions->addPostStep(function ($state) use ($columnName, $newColumn) { + $sql = preg_replace( + sprintf("/%s(?:\/\*.*?\*\/|\([^)]+\)|'[^']*?'|[^,])+([,)])/", $this->quoteColumnName($columnName)), + sprintf('%s %s$1', $this->quoteColumnName($newColumn->getName()), $this->getColumnSqlDefinition($newColumn)), + $state['createSQL'], + 1 + ); + $this->execute($sql); - if (!isset($found)) { - throw new \InvalidArgumentException(sprintf( - 'The specified column doesn\'t exist: ' . $columnName - )); - } + return $state; + }); - $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $tableName, $tmpTableName)); + return $this->copyAndDropTmpTable($instructions, $tableName); + } - $sql = preg_replace( - sprintf("/%s\s%s.*(,\s(?!')|\)$)/U", preg_quote($this->quoteColumnName($columnName)), preg_quote($columnType)), - "", - $sql - ); + /** + * {@inheritdoc} + */ + protected function getDropColumnInstructions($tableName, $columnName) + { + $instructions = $this->beginAlterByCopyTable($tableName); - if (substr($sql, -2) === ', ') { - $sql = substr($sql, 0, -2) . ')'; - } + $instructions->addPostStep(function ($state) use ($columnName) { + $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columnName, false); - $this->execute($sql); + return $newState + $state; + }); - $sql = sprintf( - 'INSERT INTO %s(%s) SELECT %s FROM %s', - $tableName, - implode(', ', $columns), - implode(', ', $columns), - $tmpTableName - ); + $instructions->addPostStep(function ($state) use ($columnName) { + $sql = preg_replace( + sprintf("/%s\s%s.*(,\s(?!')|\)$)/U", preg_quote($this->quoteColumnName($columnName)), preg_quote($state['columnType'])), + "", + $state['createSQL'] + ); - $this->execute($sql); - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName))); + if (substr($sql, -2) === ', ') { + $sql = substr($sql, 0, -2) . ')'; + } + + $this->execute($sql); + + return $state; + }); + + return $this->copyAndDropTmpTable($instructions, $tableName); } /** @@ -559,27 +616,27 @@ public function hasIndexByName($tableName, $indexName) /** * {@inheritdoc} */ - public function addIndex(Table $table, Index $index) + protected function getAddIndexInstructions(Table $table, Index $index) { $indexColumnArray = []; foreach ($index->getColumns() as $column) { $indexColumnArray[] = sprintf('`%s` ASC', $column); } $indexColumns = implode(',', $indexColumnArray); - $this->execute( - sprintf( - 'CREATE %s ON %s (%s)', - $this->getIndexSqlDefinition($table, $index), - $this->quoteTableName($table->getName()), - $indexColumns - ) + $sql = sprintf( + 'CREATE %s ON %s (%s)', + $this->getIndexSqlDefinition($table, $index), + $this->quoteTableName($table->getName()), + $indexColumns ); + + return new AlterInstructions([], [$sql]); } /** * {@inheritdoc} */ - public function dropIndex($tableName, $columns) + protected function getDropIndexByColumnsInstructions($tableName, $columns) { if (is_string($columns)) { $columns = [$columns]; // str to array @@ -587,41 +644,39 @@ public function dropIndex($tableName, $columns) $indexes = $this->getIndexes($tableName); $columns = array_map('strtolower', $columns); + $instructions = new AlterInstructions(); foreach ($indexes as $index) { $a = array_diff($columns, $index['columns']); if (empty($a)) { - $this->execute( - sprintf( - 'DROP INDEX %s', - $this->quoteColumnName($index['index']) - ) - ); - - return; + $instructions->addPostStep(sprintf( + 'DROP INDEX %s', + $this->quoteColumnName($index['index']) + )); } } + + return $instructions; } /** * {@inheritdoc} */ - public function dropIndexByName($tableName, $indexName) + protected function getDropIndexByNameInstructions($tableName, $indexName) { $indexes = $this->getIndexes($tableName); + $instructions = new AlterInstructions(); foreach ($indexes as $index) { if ($indexName === $index['index']) { - $this->execute( - sprintf( - 'DROP INDEX %s', - $this->quoteColumnName($indexName) - ) - ); - - return; + $instructions->addPostStep(sprintf( + 'DROP INDEX %s', + $this->quoteColumnName($indexName) + )); } } + + return $instructions; } /** @@ -634,12 +689,7 @@ public function hasForeignKey($tableName, $columns, $constraint = null) } $foreignKeys = $this->getForeignKeys($tableName); - $a = array_diff($columns, $foreignKeys); - if (empty($a)) { - return true; - } - - return false; + return !array_diff($columns, $foreignKeys); } /** @@ -683,103 +733,80 @@ protected function getForeignKeys($tableName) /** * {@inheritdoc} */ - public function addForeignKey(Table $table, ForeignKey $foreignKey) + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey) { - // TODO: DRY this up.... - $this->execute('pragma foreign_keys = ON'); + $instructions = $this->beginAlterByCopyTable($table->getName()); - $tmpTableName = 'tmp_' . $table->getName(); - $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\''); + $tableName = $table->getName(); + $instructions->addPostStep(function ($state) use ($foreignKey) { + $this->execute('pragma foreign_keys = ON'); + $sql = substr($state['createSQL'], 0, -1) . ',' . $this->getForeignKeySqlDefinition($foreignKey) . ')'; + $this->execute($sql); - $sql = ''; - foreach ($rows as $row) { - if ($row['tbl_name'] === $table->getName()) { - $sql = $row['sql']; - } - } + return $state; + }); - $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($table->getName()))); - $columns = []; - foreach ($rows as $column) { - $columns[] = $this->quoteColumnName($column['name']); - } - - $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($table->getName()), $tmpTableName)); + $instructions->addPostStep(function ($state) { + $columns = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($state['tmpTableName']))); + $names = array_map([$this, 'quoteColumnName'], array_column($columns, 'name')); + $selectColumns = $writeColumns = $names; - $sql = substr($sql, 0, -1) . ',' . $this->getForeignKeySqlDefinition($foreignKey) . ')'; - $this->execute($sql); - - $sql = sprintf( - 'INSERT INTO %s(%s) SELECT %s FROM %s', - $this->quoteTableName($table->getName()), - implode(', ', $columns), - implode(', ', $columns), - $this->quoteTableName($tmpTableName) - ); + return compact('selectColumns', 'writeColumns') + $state; + }); - $this->execute($sql); - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName))); + return $this->copyAndDropTmpTable($instructions, $tableName); } /** * {@inheritdoc} */ - public function dropForeignKey($tableName, $columns, $constraint = null) + protected function getDropForeignKeyInstructions($tableName, $constraint) { - // TODO: DRY this up.... - if (is_string($columns)) { - $columns = [$columns]; // str to array - } + throw new \BadMethodCallException('SQLite does not have named foreign keys'); + } - $tmpTableName = 'tmp_' . $tableName; + /** + * {@inheritdoc} + */ + protected function getDropForeignKeyByColumnsInstructions($tableName, $columns) + { + $instructions = $this->beginAlterByCopyTable($tableName); - $rows = $this->fetchAll('select * from sqlite_master where `type` = \'table\''); + $instructions->addPostStep(function ($state) use ($columns) { + $newState = $this->calculateNewTableColumns($state['tmpTableName'], $columns[0], $columns[0]); - $sql = ''; - foreach ($rows as $table) { - if ($table['tbl_name'] === $tableName) { - $sql = $table['sql']; - } - } + $selectColumns = $newState['selectColumns']; + $columns = array_map([$this, 'quoteColumnName'], $columns); + $diff = array_diff($columns, $selectColumns); - $rows = $this->fetchAll(sprintf('pragma table_info(%s)', $this->quoteTableName($tableName))); - $replaceColumns = []; - foreach ($rows as $row) { - if (!in_array($row['name'], $columns)) { - $replaceColumns[] = $row['name']; - } else { - $found = true; + if (!empty($diff)) { + throw new \InvalidArgumentException(sprintf( + 'The specified columns doen\'t exist: ' . implode(', ', $diff) + )); } - } - if (!isset($found)) { - throw new \InvalidArgumentException(sprintf( - 'The specified column doesn\'t exist: ' - )); - } + return $newState + $state; + }); - $this->execute(sprintf('ALTER TABLE %s RENAME TO %s', $this->quoteTableName($tableName), $tmpTableName)); + $instructions->addPostStep(function ($state) use ($columns) { + $sql = ''; - foreach ($columns as $columnName) { - $search = sprintf( - "/,[^,]*\(%s(?:,`?(.*)`?)?\) REFERENCES[^,]*\([^\)]*\)[^,)]*/", - $this->quoteColumnName($columnName) - ); - $sql = preg_replace($search, '', $sql, 1); - } + foreach ($columns as $columnName) { + $search = sprintf( + "/,[^,]*\(%s(?:,`?(.*)`?)?\) REFERENCES[^,]*\([^\)]*\)[^,)]*/", + $this->quoteColumnName($columnName) + ); + $sql = preg_replace($search, '', $state['createSQL'], 1); + } - $this->execute($sql); + if ($sql) { + $this->execute($sql); + } - $sql = sprintf( - 'INSERT INTO %s(%s) SELECT %s FROM %s', - $tableName, - implode(', ', $columns), - implode(', ', $columns), - $tmpTableName - ); + return $state; + }); - $this->execute($sql); - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tmpTableName))); + return $this->copyAndDropTmpTable($instructions, $tableName); } /** @@ -984,7 +1011,7 @@ protected function getCommentDefinition(Column $column) /** * Gets the SQLite Index Definition for an Index object. * - * @param \Phinx\Db\Table $table Table + * @param \Phinx\Db\Table\Table $table Table * @param \Phinx\Db\Table\Index $index Index * @return string */ diff --git a/src/Phinx/Db/Adapter/SqlServerAdapter.php b/src/Phinx/Db/Adapter/SqlServerAdapter.php index b986ac093..cddc5db8f 100644 --- a/src/Phinx/Db/Adapter/SqlServerAdapter.php +++ b/src/Phinx/Db/Adapter/SqlServerAdapter.php @@ -28,10 +28,11 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; +use Phinx\Db\Util\AlterInstructions; use Phinx\Util\Literal; /** @@ -204,12 +205,11 @@ public function hasTable($tableName) /** * {@inheritdoc} */ - public function createTable(Table $table) + public function createTable(Table $table, array $columns = [], array $indexes = []) { $options = $table->getOptions(); // Add the default primary key - $columns = $table->getPendingColumns(); if (!isset($options['id']) || (isset($options['id']) && $options['id'] === true)) { $column = new Column(); $column->setName('id') @@ -254,12 +254,6 @@ public function createTable(Table $table) $sqlBuffer[] = $pkSql; } - // set the foreign keys - $foreignKeys = $table->getForeignKeys(); - foreach ($foreignKeys as $foreignKey) { - $sqlBuffer[] = $this->getForeignKeySqlDefinition($foreignKey, $table->getName()); - } - $sql .= implode(', ', $sqlBuffer); $sql .= ');'; @@ -269,7 +263,6 @@ public function createTable(Table $table) } // set the indexes - $indexes = $table->getIndexes(); foreach ($indexes as $index) { $sql .= $this->getIndexSqlDefinition($index, $table->getName()); } @@ -307,17 +300,25 @@ protected function getColumnCommentSqlDefinition(Column $column, $tableName) /** * {@inheritdoc} */ - public function renameTable($tableName, $newTableName) + protected function getRenameTableInstructions($tableName, $newTableName) { - $this->execute(sprintf('EXEC sp_rename \'%s\', \'%s\'', $tableName, $newTableName)); + $sql = sprintf( + 'EXEC sp_rename \'%s\', \'%s\'', + $tableName, + $newTableName + ); + + return new AlterInstructions([], [$sql]); } /** * {@inheritdoc} */ - public function dropTable($tableName) + protected function getDropTableInstructions($tableName) { - $this->execute(sprintf('DROP TABLE %s', $this->quoteTableName($tableName))); + $sql = sprintf('DROP TABLE %s', $this->quoteTableName($tableName)); + + return new AlterInstructions([], [$sql]); } /** @@ -426,39 +427,29 @@ public function hasColumn($tableName, $columnName) /** * {@inheritdoc} */ - public function addColumn(Table $table, Column $column) + protected function getAddColumnInstructions(Table $table, Column $column) { - $sql = sprintf( + $alter = sprintf( 'ALTER TABLE %s ADD %s %s', - $this->quoteTableName($table->getName()), + $table->getName(), $this->quoteColumnName($column->getName()), $this->getColumnSqlDefinition($column) ); - $this->execute($sql); + return new AlterInstructions([], [$alter]); } /** * {@inheritdoc} */ - public function renameColumn($tableName, $columnName, $newColumnName) + protected function getRenameColumnInstructions($tableName, $columnName, $newColumnName) { if (!$this->hasColumn($tableName, $columnName)) { throw new \InvalidArgumentException("The specified column does not exist: $columnName"); } - $this->renameDefault($tableName, $columnName, $newColumnName); - $this->execute( - sprintf( - "EXECUTE sp_rename N'%s.%s', N'%s', 'COLUMN' ", - $tableName, - $columnName, - $newColumnName - ) - ); - } - protected function renameDefault($tableName, $columnName, $newColumnName) - { + $instructions = new AlterInstructions(); + $oldConstraintName = "DF_{$tableName}_{$columnName}"; $newConstraintName = "DF_{$tableName}_{$newColumnName}"; $sql = <<execute(sprintf( + $instructions->addPostStep(sprintf( $sql, $oldConstraintName, $newConstraintName )); + + $instructions->addPostStep(sprintf( + "EXECUTE sp_rename N'%s.%s', N'%s', 'COLUMN' ", + $tableName, + $columnName, + $newColumnName + )); + + return $instructions; } - public function changeDefault($tableName, Column $newColumn) + /** + * Returns the instructions to change a column default value + * + * @param string $tableName The table where the column is + * @param Column $newColumn The column to alter + * @return AlterInstructions + */ + protected function getChangeDefault($tableName, Column $newColumn) { $constraintName = "DF_{$tableName}_{$newColumn->getName()}"; $default = $newColumn->getDefault(); + $instructions = new AlterInstructions(); if ($default === null) { $default = 'DEFAULT NULL'; @@ -486,77 +494,88 @@ public function changeDefault($tableName, Column $newColumn) } if (empty($default)) { - return; + return $instructions; } - $this->execute(sprintf( + $instructions->addPostStep(sprintf( 'ALTER TABLE %s ADD CONSTRAINT %s %s FOR %s', $this->quoteTableName($tableName), $constraintName, $default, $this->quoteColumnName($newColumn->getName()) )); + + return $instructions; } /** * {@inheritdoc} */ - public function changeColumn($tableName, $columnName, Column $newColumn) + protected function getChangeColumnInstructions($tableName, $columnName, Column $newColumn) { $columns = $this->getColumns($tableName); - $changeDefault = $newColumn->getDefault() !== $columns[$columnName]->getDefault() || $newColumn->getType() !== $columns[$columnName]->getType(); + $changeDefault = + $newColumn->getDefault() !== $columns[$columnName]->getDefault() || + $newColumn->getType() !== $columns[$columnName]->getType(); + + $instructions = new AlterInstructions(); + if ($columnName !== $newColumn->getName()) { - $this->renameColumn($tableName, $columnName, $newColumn->getName()); + $instructions->merge( + $this->getRenameColumnInstructions($tableName, $columnName, $newColumn->getName()) + ); } if ($changeDefault) { - $this->dropDefaultConstraint($tableName, $newColumn->getName()); + $instructions->merge($this->getDropDefaultConstraint($tableName, $newColumn->getName())); } - $this->execute( - sprintf( - 'ALTER TABLE %s ALTER COLUMN %s %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($newColumn->getName()), - $this->getColumnSqlDefinition($newColumn, false) - ) - ); + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s ALTER COLUMN %s %s', + $this->quoteTableName($tableName), + $this->quoteColumnName($newColumn->getName()), + $this->getColumnSqlDefinition($newColumn, false) + )); // change column comment if needed if ($newColumn->getComment()) { - $sql = $this->getColumnCommentSqlDefinition($newColumn, $tableName); - $this->execute($sql); + $instructions->merge($this->getColumnCommentSqlDefinition($newColumn, $tableName)); } if ($changeDefault) { - $this->changeDefault($tableName, $newColumn); + $instructions->merge($this->getChangeDefault($tableName, $newColumn)); } + + return $instructions; } /** * {@inheritdoc} */ - public function dropColumn($tableName, $columnName) + protected function getDropColumnInstructions($tableName, $columnName) { - $this->dropDefaultConstraint($tableName, $columnName); + $instructions = $this->getDropDefaultConstraint($tableName, $columnName); - $this->execute( - sprintf( - 'ALTER TABLE %s DROP COLUMN %s', - $this->quoteTableName($tableName), - $this->quoteColumnName($columnName) - ) - ); + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s DROP COLUMN %s', + $this->quoteTableName($tableName), + $this->quoteColumnName($columnName) + )); + + return $instructions; } - protected function dropDefaultConstraint($tableName, $columnName) + /** + * {@inheritdoc} + */ + protected function getDropDefaultConstraint($tableName, $columnName) { $defaultConstraint = $this->getDefaultConstraint($tableName, $columnName); if (!$defaultConstraint) { - return; + return new AlterInstructions(); } - $this->dropForeignKey($tableName, $columnName, $defaultConstraint); + return $this->getDropForeignKeyInstructions($tableName, $defaultConstraint); } protected function getDefaultConstraint($tableName, $columnName) @@ -671,16 +690,17 @@ public function hasIndexByName($tableName, $indexName) /** * {@inheritdoc} */ - public function addIndex(Table $table, Index $index) + protected function getAddIndexInstructions(Table $table, Index $index) { $sql = $this->getIndexSqlDefinition($index, $table->getName()); - $this->execute($sql); + + return new AlterInstructions([], [$sql]); } /** * {@inheritdoc} */ - public function dropIndex($tableName, $columns) + protected function getDropIndexByColumnsInstructions($tableName, $columns) { if (is_string($columns)) { $columns = [$columns]; // str to array @@ -688,43 +708,51 @@ public function dropIndex($tableName, $columns) $indexes = $this->getIndexes($tableName); $columns = array_map('strtolower', $columns); + $instructions = new AlterInstructions(); foreach ($indexes as $indexName => $index) { $a = array_diff($columns, $index['columns']); if (empty($a)) { - $this->execute( - sprintf( - 'DROP INDEX %s ON %s', - $this->quoteColumnName($indexName), - $this->quoteTableName($tableName) - ) - ); + $instructions->addPostStep(sprintf( + 'DROP INDEX %s ON %s', + $this->quoteColumnName($indexName), + $this->quoteTableName($tableName) + )); - return; + return $instructions; } } + + throw new \InvalidArgumentException(sprintf( + "The specified index on columns '%s' does not exist", + implode(',', $columns) + )); } /** * {@inheritdoc} */ - public function dropIndexByName($tableName, $indexName) + protected function getDropIndexByNameInstructions($tableName, $indexName) { $indexes = $this->getIndexes($tableName); + $instructions = new AlterInstructions(); foreach ($indexes as $name => $index) { if ($name === $indexName) { - $this->execute( - sprintf( - 'DROP INDEX %s ON %s', - $this->quoteColumnName($indexName), - $this->quoteTableName($tableName) - ) - ); + $instructions->addPostStep(sprintf( + 'DROP INDEX %s ON %s', + $this->quoteColumnName($indexName), + $this->quoteTableName($tableName) + )); - return; + return $instructions; } } + + throw new \InvalidArgumentException(sprintf( + "The specified index name '%s' does not exist", + $indexName + )); } /** @@ -790,58 +818,64 @@ protected function getForeignKeys($tableName) /** * {@inheritdoc} */ - public function addForeignKey(Table $table, ForeignKey $foreignKey) + protected function getAddForeignKeyInstructions(Table $table, ForeignKey $foreignKey) { - $this->execute( - sprintf( - 'ALTER TABLE %s ADD %s', - $this->quoteTableName($table->getName()), - $this->getForeignKeySqlDefinition($foreignKey, $table->getName()) - ) - ); + $instructions = new AlterInstructions(); + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s ADD %s', + $this->quoteTableName($table->getName()), + $this->getForeignKeySqlDefinition($foreignKey, $table->getName()) + )); + + return $instructions; } /** * {@inheritdoc} */ - public function dropForeignKey($tableName, $columns, $constraint = null) + protected function getDropForeignKeyInstructions($tableName, $constraint) { - if (is_string($columns)) { - $columns = [$columns]; // str to array - } + $instructions = new AlterInstructions(); + $instructions->addPostStep(sprintf( + 'ALTER TABLE %s DROP CONSTRAINT %s', + $this->quoteTableName($tableName), + $constraint + )); - if ($constraint) { - $this->execute( - sprintf( - 'ALTER TABLE %s DROP CONSTRAINT %s', - $this->quoteTableName($tableName), - $constraint - ) - ); + return $instructions; + } - return; - } else { - foreach ($columns as $column) { - $rows = $this->fetchAll(sprintf( - "SELECT - tc.constraint_name, - tc.table_name, kcu.column_name, - ccu.table_name AS referenced_table_name, - ccu.column_name AS referenced_column_name - FROM - information_schema.table_constraints AS tc - JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name - JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name - WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s' and ccu.column_name='%s' - ORDER BY kcu.ordinal_position", - $tableName, - $column - )); - foreach ($rows as $row) { - $this->dropForeignKey($tableName, $columns, $row['constraint_name']); - } + /** + * {@inheritdoc} + */ + protected function getDropForeignKeyByColumnsInstructions($tableName, $columns) + { + $instructions = new AlterInstructions(); + + foreach ($columns as $column) { + $rows = $this->fetchAll(sprintf( + "SELECT + tc.constraint_name, + tc.table_name, kcu.column_name, + ccu.table_name AS referenced_table_name, + ccu.column_name AS referenced_column_name + FROM + information_schema.table_constraints AS tc + JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name + JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name + WHERE constraint_type = 'FOREIGN KEY' AND tc.table_name = '%s' and ccu.column_name='%s' + ORDER BY kcu.ordinal_position", + $tableName, + $column + )); + foreach ($rows as $row) { + $instructions->merge( + $this->getDropForeignKeyInstructions($tableName, $row['constraint_name']) + ); } } + + return $instructions; } /** @@ -1013,6 +1047,7 @@ protected function getColumnSqlDefinition(Column $column, $create = true) if ($column->getPrecision() && $column->getScale()) { $buffer[] = '(' . $column->getPrecision() . ',' . $column->getScale() . ')'; } + $properties = $column->getProperties(); $buffer[] = $column->getType() === 'filestream' ? 'FILESTREAM' : ''; $buffer[] = isset($properties['rowguidcol']) ? 'ROWGUIDCOL' : ''; diff --git a/src/Phinx/Db/Adapter/TablePrefixAdapter.php b/src/Phinx/Db/Adapter/TablePrefixAdapter.php index 4caa7fbce..ee2c00a4f 100644 --- a/src/Phinx/Db/Adapter/TablePrefixAdapter.php +++ b/src/Phinx/Db/Adapter/TablePrefixAdapter.php @@ -28,10 +28,20 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; +use Phinx\Db\Action\AddColumn; +use Phinx\Db\Action\AddForeignKey; +use Phinx\Db\Action\AddIndex; +use Phinx\Db\Action\ChangeColumn; +use Phinx\Db\Action\DropForeignKey; +use Phinx\Db\Action\DropIndex; +use Phinx\Db\Action\DropTable; +use Phinx\Db\Action\RemoveColumn; +use Phinx\Db\Action\RenameColumn; +use Phinx\Db\Action\RenameTable; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; /** * Table prefix/suffix adapter. @@ -40,7 +50,7 @@ * * @author Samuel Fisher */ -class TablePrefixAdapter extends AdapterWrapper +class TablePrefixAdapter extends AdapterWrapper implements DirectActionInterface { /** * {@inheritdoc} @@ -63,19 +73,13 @@ public function hasTable($tableName) /** * {@inheritdoc} */ - public function createTable(Table $table) + public function createTable(Table $table, array $columns = [], array $indexes = []) { - $adapterTable = clone $table; - $adapterTableName = $this->getAdapterTableName($table->getName()); - $adapterTable->setName($adapterTableName); - - foreach ($adapterTable->getForeignKeys() as $fk) { - $adapterReferenceTable = $fk->getReferencedTable(); - $adapterReferenceTableName = $this->getAdapterTableName($adapterReferenceTable->getName()); - $adapterReferenceTable->setName($adapterReferenceTableName); - } - - parent::createTable($adapterTable); + $adapterTable = new Table( + $this->getAdapterTableName($table->getName()), + $table->getOptions() + ); + parent::createTable($adapterTable, $columns, $indexes); } /** @@ -83,9 +87,14 @@ public function createTable(Table $table) */ public function renameTable($tableName, $newTableName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTableName = $this->getAdapterTableName($tableName); $adapterNewTableName = $this->getAdapterTableName($newTableName); - parent::renameTable($adapterTableName, $adapterNewTableName); + $adapter->renameTable($adapterTableName, $adapterNewTableName); } /** @@ -93,8 +102,12 @@ public function renameTable($tableName, $newTableName) */ public function dropTable($tableName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($tableName); - parent::dropTable($adapterTableName); + $adapter->dropTable($adapterTableName); } /** @@ -131,10 +144,13 @@ public function hasColumn($tableName, $columnName) */ public function addColumn(Table $table, Column $column) { - $adapterTable = clone $table; + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($table->getName()); - $adapterTable->setName($adapterTableName); - parent::addColumn($adapterTable, $column); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + $adapter->addColumn($adapterTable, $column); } /** @@ -142,8 +158,12 @@ public function addColumn(Table $table, Column $column) */ public function renameColumn($tableName, $columnName, $newColumnName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($tableName); - parent::renameColumn($adapterTableName, $columnName, $newColumnName); + $adapter->renameColumn($adapterTableName, $columnName, $newColumnName); } /** @@ -151,9 +171,12 @@ public function renameColumn($tableName, $columnName, $newColumnName) */ public function changeColumn($tableName, $columnName, Column $newColumn) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($tableName); - - return parent::changeColumn($adapterTableName, $columnName, $newColumn); + $adapter->changeColumn($adapterTableName, $columnName, $newColumn); } /** @@ -161,8 +184,12 @@ public function changeColumn($tableName, $columnName, Column $newColumn) */ public function dropColumn($tableName, $columnName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($tableName); - parent::dropColumn($adapterTableName, $columnName); + $adapter->dropColumn($adapterTableName, $columnName); } /** @@ -190,10 +217,12 @@ public function hasIndexByName($tableName, $indexName) */ public function addIndex(Table $table, Index $index) { - $adapterTable = clone $table; - $adapterTableName = $this->getAdapterTableName($table->getName()); - $adapterTable->setName($adapterTableName); - parent::addIndex($adapterTable, $index); + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } + $adapterTable = new Table($table->getName(), $table->getOptions()); + $adapter->addIndex($adapterTable, $index); } /** @@ -201,8 +230,12 @@ public function addIndex(Table $table, Index $index) */ public function dropIndex($tableName, $columns) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($tableName); - parent::dropIndex($adapterTableName, $columns); + $adapter->dropIndex($adapterTableName, $columns); } /** @@ -210,8 +243,12 @@ public function dropIndex($tableName, $columns) */ public function dropIndexByName($tableName, $indexName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($tableName); - parent::dropIndexByName($adapterTableName, $indexName); + $adapter->dropIndexByName($adapterTableName, $indexName); } /** @@ -229,10 +266,13 @@ public function hasForeignKey($tableName, $columns, $constraint = null) */ public function addForeignKey(Table $table, ForeignKey $foreignKey) { - $adapterTable = clone $table; + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($table->getName()); - $adapterTable->setName($adapterTableName); - parent::addForeignKey($adapterTable, $foreignKey); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + $adapter->addForeignKey($adapterTable, $foreignKey); } /** @@ -240,8 +280,12 @@ public function addForeignKey(Table $table, ForeignKey $foreignKey) */ public function dropForeignKey($tableName, $columns, $constraint = null) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The underlying adapter does not implement DirectActionInterface'); + } $adapterTableName = $this->getAdapterTableName($tableName); - parent::dropForeignKey($adapterTableName, $columns, $constraint); + $adapter->dropForeignKey($adapterTableName, $columns, $constraint); } /** @@ -249,9 +293,8 @@ public function dropForeignKey($tableName, $columns, $constraint = null) */ public function insert(Table $table, $row) { - $adapterTable = clone $table; $adapterTableName = $this->getAdapterTableName($table->getName()); - $adapterTable->setName($adapterTableName); + $adapterTable = new Table($adapterTableName, $table->getOptions()); parent::insert($adapterTable, $row); } @@ -260,9 +303,8 @@ public function insert(Table $table, $row) */ public function bulkinsert(Table $table, $rows) { - $adapterTable = clone $table; $adapterTableName = $this->getAdapterTableName($table->getName()); - $adapterTable->setName($adapterTableName); + $adapterTable = new Table($adapterTableName, $table->getOptions()); parent::bulkinsert($adapterTable, $rows); } @@ -296,4 +338,68 @@ public function getAdapterTableName($tableName) { return $this->getPrefix() . $tableName . $this->getSuffix(); } + + /** + * {@inheritdoc} + */ + public function executeActions(Table $table, array $actions) + { + $adapterTableName = $this->getAdapterTableName($table->getName()); + $adapterTable = new Table($adapterTableName, $table->getOptions()); + + foreach ($actions as $k => $action) { + switch (true) { + case ($action instanceof AddColumn): + $actions[$k] = new AddColumn($adapterTable, $action->getColumn()); + break; + + case ($action instanceof AddIndex): + $actions[$k] = new AddIndex($adapterTable, $action->getIndex()); + break; + + case ($action instanceof AddForeignKey): + $foreignKey = clone $action->getForeignKey(); + $refTable = $foreignKey->getReferencedTable(); + $refTableName = $this->getAdapterTableName($refTable->getName()); + $foreignKey->setReferencedTable(new Table($refTableName, $refTable->getOptions())); + $actions[$k] = new AddForeignKey($adapterTable, $foreignKey); + break; + + case ($action instanceof ChangeColumn): + $actions[$k] = new ChangeColumn($adapterTable, $action->getColumnName(), $action->getColumn()); + break; + + case ($action instanceof DropForeignKey): + $actions[$k] = new DropForeignKey($adapterTable, $action->getForeignKey()); + break; + + case ($action instanceof DropIndex): + $actions[$k] = new DropIndex($adapterTable, $action->getIndex()); + break; + + case ($action instanceof DropTable): + $actions[$k] = new DropTable($adapterTable); + break; + + case ($action instanceof RemoveColumn): + $actions[$k] = new RemoveColumn($adapterTable, $action->getColumn()); + break; + + case ($action instanceof RenameColumn): + $actions[$k] = new RenameColumn($adapterTable, $action->getColumn(), $action->getNewName()); + break; + + case ($action instanceof RenameTable): + $actions[$k] = new RenameTable($adapterTable, $action->getNewName()); + break; + + default: + throw new \InvalidArgumentException( + sprintf("Forgot to implement table prefixing for action: '%s'", get_class($action)) + ); + } + } + + parent::executeActions($adapterTable, $actions); + } } diff --git a/src/Phinx/Db/Adapter/TimedOutputAdapter.php b/src/Phinx/Db/Adapter/TimedOutputAdapter.php index fbe00ba35..b0c911ccd 100644 --- a/src/Phinx/Db/Adapter/TimedOutputAdapter.php +++ b/src/Phinx/Db/Adapter/TimedOutputAdapter.php @@ -28,16 +28,16 @@ */ namespace Phinx\Db\Adapter; -use Phinx\Db\Table; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table; use Symfony\Component\Console\Output\OutputInterface; /** * Wraps any adpter to record the time spend executing its commands */ -class TimedOutputAdapter extends AdapterWrapper +class TimedOutputAdapter extends AdapterWrapper implements DirectActionInterface { /** @@ -131,11 +131,11 @@ public function bulkinsert(Table $table, $rows) /** * {@inheritdoc} */ - public function createTable(Table $table) + public function createTable(Table $table, array $columns = [], array $indexes = []) { $end = $this->startCommandTimer(); $this->writeCommand('createTable', [$table->getName()]); - parent::createTable($table); + parent::createTable($table, $columns, $indexes); $end(); } @@ -144,9 +144,13 @@ public function createTable(Table $table) */ public function renameTable($tableName, $newTableName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('renameTable', [$tableName, $newTableName]); - parent::renameTable($tableName, $newTableName); + $adapter->renameTable($tableName, $newTableName); $end(); } @@ -155,9 +159,13 @@ public function renameTable($tableName, $newTableName) */ public function dropTable($tableName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('dropTable', [$tableName]); - parent::dropTable($tableName); + $adapter->dropTable($tableName); $end(); } @@ -177,6 +185,10 @@ public function truncateTable($tableName) */ public function addColumn(Table $table, Column $column) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand( 'addColumn', @@ -186,7 +198,7 @@ public function addColumn(Table $table, Column $column) $column->getType() ] ); - parent::addColumn($table, $column); + $adapter->addColumn($table, $column); $end(); } @@ -195,9 +207,13 @@ public function addColumn(Table $table, Column $column) */ public function renameColumn($tableName, $columnName, $newColumnName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('renameColumn', [$tableName, $columnName, $newColumnName]); - parent::renameColumn($tableName, $columnName, $newColumnName); + $adapter->renameColumn($tableName, $columnName, $newColumnName); $end(); } @@ -206,9 +222,13 @@ public function renameColumn($tableName, $columnName, $newColumnName) */ public function changeColumn($tableName, $columnName, Column $newColumn) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('changeColumn', [$tableName, $columnName, $newColumn->getType()]); - parent::changeColumn($tableName, $columnName, $newColumn); + $adapter->changeColumn($tableName, $columnName, $newColumn); $end(); } @@ -217,9 +237,13 @@ public function changeColumn($tableName, $columnName, Column $newColumn) */ public function dropColumn($tableName, $columnName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('dropColumn', [$tableName, $columnName]); - parent::dropColumn($tableName, $columnName); + $adapter->dropColumn($tableName, $columnName); $end(); } @@ -228,9 +252,13 @@ public function dropColumn($tableName, $columnName) */ public function addIndex(Table $table, Index $index) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('addIndex', [$table->getName(), $index->getColumns()]); - parent::addIndex($table, $index); + $adapter->addIndex($table, $index); $end(); } @@ -239,9 +267,13 @@ public function addIndex(Table $table, Index $index) */ public function dropIndex($tableName, $columns) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('dropIndex', [$tableName, $columns]); - parent::dropIndex($tableName, $columns); + $adapter->dropIndex($tableName, $columns); $end(); } @@ -250,9 +282,13 @@ public function dropIndex($tableName, $columns) */ public function dropIndexByName($tableName, $indexName) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('dropIndexByName', [$tableName, $indexName]); - parent::dropIndexByName($tableName, $indexName); + $adapter->dropIndexByName($tableName, $indexName); $end(); } @@ -261,9 +297,13 @@ public function dropIndexByName($tableName, $indexName) */ public function addForeignKey(Table $table, ForeignKey $foreignKey) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('addForeignKey', [$table->getName(), $foreignKey->getColumns()]); - parent::addForeignKey($table, $foreignKey); + $adapter->addForeignKey($table, $foreignKey); $end(); } @@ -272,9 +312,13 @@ public function addForeignKey(Table $table, ForeignKey $foreignKey) */ public function dropForeignKey($tableName, $columns, $constraint = null) { + $adapter = $this->getAdapter(); + if (!$adapter instanceof DirectActionInterface) { + throw new \BadMethodCallException('The adapter needs to implement DirectActionInterface'); + } $end = $this->startCommandTimer(); $this->writeCommand('dropForeignKey', [$tableName, $columns]); - parent::dropForeignKey($tableName, $columns, $constraint); + $adapter->dropForeignKey($tableName, $columns, $constraint); $end(); } @@ -321,4 +365,15 @@ public function dropSchema($name) parent::dropSchema($name); $end(); } + + /** + * {@inheritdoc} + */ + public function executeActions(Table $table, array $actions) + { + $end = $this->startCommandTimer(); + $this->writeCommand(sprintf('Altering table %s', $table->getName())); + parent::executeActions($table, $actions); + $end(); + } } diff --git a/src/Phinx/Db/Plan/AlterTable.php b/src/Phinx/Db/Plan/AlterTable.php new file mode 100644 index 000000000..a52106dfc --- /dev/null +++ b/src/Phinx/Db/Plan/AlterTable.php @@ -0,0 +1,90 @@ +table = $table; + } + + /** + * Adds another action to the collection + * + * @param Action $action The action to add + * @return void + */ + public function addAction(Action $action) + { + $this->actions[] = $action; + } + + /** + * Returns the table associated to this collection + * + * @return Table + */ + public function getTable() + { + return $this->table; + } + + /** + * Returns an array with all collected actions + * + * @return Action[] + */ + public function getActions() + { + return $this->actions; + } +} diff --git a/src/Phinx/Db/Plan/Intent.php b/src/Phinx/Db/Plan/Intent.php new file mode 100644 index 000000000..fe90e5721 --- /dev/null +++ b/src/Phinx/Db/Plan/Intent.php @@ -0,0 +1,74 @@ +actions[] = $action; + } + + /** + * Returns the full list of actions + * + * @return Action[] + */ + public function getActions() + { + return $this->actions; + } + + /** + * Merges another Intent object with this one + * + * @param Intent $another The other intent to merge in + * @return void + */ + public function merge(Intent $another) + { + $this->actions = array_merge($this->actions, $another->getActions()); + } +} diff --git a/src/Phinx/Db/Plan/NewTable.php b/src/Phinx/Db/Plan/NewTable.php new file mode 100644 index 000000000..8b8947d69 --- /dev/null +++ b/src/Phinx/Db/Plan/NewTable.php @@ -0,0 +1,119 @@ +table = $table; + } + + /** + * Adds a column to the collection + * + * @param Column $column The column description + * @return void + */ + public function addColumn(Column $column) + { + $this->columns[] = $column; + } + + /** + * Adds an index to the collection + * + * @param Index $index The index description + * @return void + */ + public function addIndex(Index $index) + { + $this->indexes[] = $index; + } + + /** + * Retunrns the table object associated to this collection + * + * @return Table + */ + public function getTable() + { + return $this->table; + } + + /** + * Returns the columns collection + * + * @return Column[] + */ + public function getColumns() + { + return $this->columns; + } + + /** + * Returns the indexes collection + * + * @return Index[] + */ + public function getIndexes() + { + return $this->indexes; + } +} diff --git a/src/Phinx/Db/Plan/Plan.php b/src/Phinx/Db/Plan/Plan.php new file mode 100644 index 000000000..237e8333c --- /dev/null +++ b/src/Phinx/Db/Plan/Plan.php @@ -0,0 +1,357 @@ +createPlan($intent->getActions()); + } + + /** + * Parses the given Intent and creates the separate steps to execute + * + * @param Intent $actions The actions to use for the plan + * @return void + */ + protected function createPlan($actions) + { + $this->gatherCreates($actions); + $this->gatherUpdates($actions); + $this->gatherTableMoves($actions); + $this->gatherIndexes($actions); + $this->gatherConstraints($actions); + $this->resolveConflicts(); + } + + /** + * Returns a nested list of all the steps to execute + * + * @return AlterTable[][] + */ + protected function updatesSequence() + { + return [ + $this->tableUpdates, + $this->constraints, + $this->indexes, + $this->tableMoves, + ]; + } + + /** + * Executes this plan using the given AdapterInterface + * + * @param AdapterInterface $executor The executor object for the plan + * @return void + */ + public function execute(AdapterInterface $executor) + { + foreach ($this->tableCreates as $newTable) { + $executor->createTable($newTable->getTable(), $newTable->getColumns(), $newTable->getIndexes()); + } + + collection($this->updatesSequence()) + ->unfold() + ->each(function ($updates) use ($executor) { + $executor->executeActions($updates->getTable(), $updates->getActions()); + }); + } + + /** + * Executes the inverse plan (rollback the actions) with the given AdapterInterface:w + * + * @param AdapterInterface $executor The executor object for the plan + * @return void + */ + public function executeInverse(AdapterInterface $executor) + { + collection(array_reverse($this->updatesSequence())) + ->unfold() + ->each(function ($updates) use ($executor) { + $executor->executeActions($updates->getTable(), $updates->getActions()); + }); + + foreach ($this->tableCreates as $newTable) { + $executor->createTable($newTable->getTable(), $newTable->getColumns(), $newTable->getIndexes()); + } + } + + /** + * Deletes certain actions from the plan if they are found to be conflicting or redundant. + * + * @return void + */ + protected function resolveConflicts() + { + $actions = collection($this->tableMoves) + ->unfold(function ($move) { + return $move->getActions(); + }); + + foreach ($actions as $action) { + if ($action instanceof DropTable) { + $this->tableUpdates = $this->forgetTable($action->getTable(), $this->tableUpdates); + $this->constraints = $this->forgetTable($action->getTable(), $this->constraints); + $this->indexes = $this->forgetTable($action->getTable(), $this->indexes); + } + } + } + + /** + * Deletes all actions related to the given table and keeps the + * rest + * + * @param Table $table The table to find in the list of actions + * @param AlterTable[] $actions The actions to transform + * @return AlterTable[] The list of actions without actions for the given table + */ + protected function forgetTable(Table $table, $actions) + { + $result = []; + foreach ($actions as $action) { + if ($action->getTable()->getName() === $table->getName()) { + continue; + } + $result[] = $action; + } + + return $result; + } + + /** + * Collects all table creation actions from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherCreates($actions) + { + collection($actions) + ->filter(function ($action) { + return $action instanceof CreateTable; + }) + ->map(function ($action) { + return [$action->getTable()->getName(), new NewTable($action->getTable())]; + }) + ->each(function ($step) { + $this->tableCreates[$step[0]] = $step[1]; + }); + + collection($actions) + ->filter(function ($action) { + return $action instanceof AddColumn + || $action instanceof AddIndex; + }) + ->filter(function ($action) { + return isset($this->tableCreates[$action->getTable()->getName()]); + }) + ->each(function ($action) { + $table = $action->getTable(); + + if ($action instanceof AddColumn) { + $this->tableCreates[$table->getName()]->addColumn($action->getColumn()); + } + + if ($action instanceof AddIndex) { + $this->tableCreates[$table->getName()]->addIndex($action->getIndex()); + } + }); + } + + /** + * Collects all alter table actions from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherUpdates($actions) + { + collection($actions) + ->filter(function ($action) { + return $action instanceof AddColumn + || $action instanceof ChangeColumn + || $action instanceof RemoveColumn + || $action instanceof RenameColumn; + }) + // We are only concerned with table changes + ->reject(function ($action) { + return isset($this->tableCreates[$action->getTable()->getName()]); + }) + ->each(function ($action) { + $table = $action->getTable(); + $name = $table->getName(); + + if (!isset($this->tableUpdates[$name])) { + $this->tableUpdates[$name] = new AlterTable($table); + } + + $this->tableUpdates[$name]->addAction($action); + }); + } + + /** + * Collects all alter table drop and renames from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherTableMoves($actions) + { + collection($actions) + ->filter(function ($action) { + return $action instanceof DropTable + || $action instanceof RenameTable; + }) + ->each(function ($action) { + $table = $action->getTable(); + $name = $table->getName(); + + if (!isset($this->tableMoves[$name])) { + $this->tableMoves[$name] = new AlterTable($table); + } + + $this->tableMoves[$name]->addAction($action); + }); + } + + /** + * Collects all index creation and drops from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherIndexes($actions) + { + collection($actions) + ->filter(function ($action) { + return $action instanceof AddIndex + || $action instanceof DropIndex; + }) + ->reject(function ($action) { + // Indexes for new tables are created inline + // so we don't wan't them here too + return isset($this->tableCreates[$action->getTable()->getName()]); + }) + ->each(function ($action) { + $table = $action->getTable(); + $name = $table->getName(); + + if (!isset($this->indexes[$name])) { + $this->indexes[$name] = new AlterTable($table); + } + + $this->indexes[$name]->addAction($action); + }); + } + + /** + * Collects all foreign key creation and drops from the given intent + * + * @param \Phinx\Db\Action\Action[] $actions The actions to parse + * @return void + */ + protected function gatherConstraints($actions) + { + collection($actions) + ->filter(function ($action) { + return $action instanceof AddForeignKey + || $action instanceof DropForeignKey; + }) + ->each(function ($action) { + $table = $action->getTable(); + $name = $table->getName(); + + if (!isset($this->constraints[$name])) { + $this->constraints[$name] = new AlterTable($table); + } + + $this->constraints[$name]->addAction($action); + }); + } +} diff --git a/src/Phinx/Db/Table.php b/src/Phinx/Db/Table.php index c5de8fe4d..33e0f1f62 100644 --- a/src/Phinx/Db/Table.php +++ b/src/Phinx/Db/Table.php @@ -28,10 +28,25 @@ */ namespace Phinx\Db; +use Phinx\Db\Action\AddColumn; +use Phinx\Db\Action\AddForeignKey; +use Phinx\Db\Action\AddIndex; +use Phinx\Db\Action\ChangeColumn; +use Phinx\Db\Action\CreateTable; +use Phinx\Db\Action\DropColumn; +use Phinx\Db\Action\DropForeignKey; +use Phinx\Db\Action\DropIndex; +use Phinx\Db\Action\DropTable; +use Phinx\Db\Action\RemoveColumn; +use Phinx\Db\Action\RenameColumn; +use Phinx\Db\Action\RenameTable; use Phinx\Db\Adapter\AdapterInterface; +use Phinx\Db\Plan\Intent; +use Phinx\Db\Plan\Plan; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; use Phinx\Db\Table\Index; +use Phinx\Db\Table\Table as TableValue; /** * @@ -40,34 +55,20 @@ class Table { /** - * @var string + * @var \Phinx\Db\Table\Table */ - protected $name; - - /** - * @var array - */ - protected $options = []; + protected $table; /** * @var \Phinx\Db\Adapter\AdapterInterface */ protected $adapter; - /** - * @var array - */ - protected $columns = []; /** - * @var array + * @var \Phinx\Db\Plan\Intent */ - protected $indexes = []; - - /** - * @var \Phinx\Db\Table\ForeignKey[] - */ - protected $foreignKeys = []; + protected $actions; /** * @var array @@ -83,27 +84,14 @@ class Table */ public function __construct($name, $options = [], AdapterInterface $adapter = null) { - $this->setName($name); - $this->setOptions($options); + $this->table = new TableValue($name, $options); + $this->actions = new Intent(); if ($adapter !== null) { $this->setAdapter($adapter); } } - /** - * Sets the table name. - * - * @param string $name Table Name - * @return \Phinx\Db\Table - */ - public function setName($name) - { - $this->name = $name; - - return $this; - } - /** * Gets the table name. * @@ -111,30 +99,27 @@ public function setName($name) */ public function getName() { - return $this->name; + return $this->table->getName(); } /** - * Sets the table options. + * Gets the table options. * - * @param array $options - * @return \Phinx\Db\Table + * @return array */ - public function setOptions($options) + public function getOptions() { - $this->options = $options; - - return $this; + return $this->table->getOptions(); } /** - * Gets the table options. + * Gets the table name and options as an object * - * @return array + * @return \Phinx\Db\Table\Table */ - public function getOptions() + public function getTable() { - return $this->options; + return $this->table; } /** @@ -157,6 +142,10 @@ public function setAdapter(AdapterInterface $adapter) */ public function getAdapter() { + if (!$this->adapter) { + throw new \RuntimeException('There is no database adapter set yet, cannot proceed'); + } + return $this->adapter; } @@ -173,11 +162,13 @@ public function exists() /** * Drops the database table. * - * @return void + * @return \Phinx\Db\Table */ public function drop() { - $this->getAdapter()->dropTable($this->getName()); + $this->actions->addAction(new DropTable($this->table)); + + return $this; } /** @@ -188,23 +179,7 @@ public function drop() */ public function rename($newTableName) { - $this->getAdapter()->renameTable($this->getName(), $newTableName); - $this->setName($newTableName); - - return $this; - } - - /** - * Sets an array of columns waiting to be committed. - * Use setPendingColumns - * - * @deprecated - * @param array $columns Columns - * @return \Phinx\Db\Table - */ - public function setColumns($columns) - { - $this->setPendingColumns($columns); + $this->actions->addAction(new RenameTable($this->table, $newTableName)); return $this; } @@ -219,75 +194,6 @@ public function getColumns() return $this->getAdapter()->getColumns($this->getName()); } - /** - * Sets an array of columns waiting to be committed. - * - * @param array $columns Columns - * @return \Phinx\Db\Table - */ - public function setPendingColumns($columns) - { - $this->columns = $columns; - - return $this; - } - - /** - * Gets an array of columns waiting to be committed. - * - * @return \Phinx\Db\Table\Column[] - */ - public function getPendingColumns() - { - return $this->columns; - } - - /** - * Sets an array of columns waiting to be indexed. - * - * @param array $indexes Indexes - * @return \Phinx\Db\Table - */ - public function setIndexes($indexes) - { - $this->indexes = $indexes; - - return $this; - } - - /** - * Gets an array of indexes waiting to be committed. - * - * @return array - */ - public function getIndexes() - { - return $this->indexes; - } - - /** - * Sets an array of foreign keys waiting to be commited. - * - * @param \Phinx\Db\Table\ForeignKey[] $foreignKeys foreign keys - * @return \Phinx\Db\Table - */ - public function setForeignKeys($foreignKeys) - { - $this->foreignKeys = $foreignKeys; - - return $this; - } - - /** - * Gets an array of foreign keys waiting to be commited. - * - * @return array|\Phinx\Db\Table\ForeignKey[] - */ - public function getForeignKeys() - { - return $this->foreignKeys; - } - /** * Sets an array of data to be inserted. * @@ -318,9 +224,7 @@ public function getData() */ public function reset() { - $this->setPendingColumns([]); - $this->setIndexes([]); - $this->setForeignKeys([]); + $this->actions = new Intent(); $this->setData([]); } @@ -341,31 +245,22 @@ public function reset() */ public function addColumn($columnName, $type = null, $options = []) { - // we need an adapter set to add a column - if ($this->getAdapter() === null) { - throw new \RuntimeException('An adapter must be specified to add a column.'); - } - - // create a new column object if only strings were supplied - if (!$columnName instanceof Column) { - $column = new Column(); - $column->setName($columnName); - $column->setType($type); - $column->setOptions($options); // map options to column methods + if ($columnName instanceof Column) { + $action = new AddColumn($this->table, $columnName); } else { - $column = $columnName; + $action = AddColumn::build($this->table, $columnName, $type, $options); } // Delegate to Adapters to check column type - if (!$this->getAdapter()->isValidColumnType($column)) { + if (!$this->getAdapter()->isValidColumnType($action->getColumn())) { throw new \InvalidArgumentException(sprintf( 'An invalid column type "%s" was specified for column "%s".', - $column->getType(), - $column->getName() + $type, + $action->getColumn()->getName() )); } - $this->columns[] = $column; + $this->actions->addAction($action); return $this; } @@ -378,7 +273,8 @@ public function addColumn($columnName, $type = null, $options = []) */ public function removeColumn($columnName) { - $this->getAdapter()->dropColumn($this->getName(), $columnName); + $action = RemoveColumn::build($this->table, $columnName); + $this->actions->addAction($action); return $this; } @@ -392,7 +288,8 @@ public function removeColumn($columnName) */ public function renameColumn($oldName, $newName) { - $this->getAdapter()->renameColumn($this->getName(), $oldName, $newName); + $action = RenameColumn::build($this->table, $oldName, $newName); + $this->actions->addAction($action); return $this; } @@ -405,23 +302,14 @@ public function renameColumn($oldName, $newName) * @param array $options Options * @return \Phinx\Db\Table */ - public function changeColumn($columnName, $newColumnType, $options = []) + public function changeColumn($columnName, $newColumnType, array $options = []) { - // create a column object if one wasn't supplied - if (!$newColumnType instanceof Column) { - $newColumn = new Column(); - $newColumn->setType($newColumnType); - $newColumn->setOptions($options); + if ($newColumnType instanceof Column) { + $action = new ChangeColumn($this->table, $columnName, $newColumnType); } else { - $newColumn = $newColumnType; + $action = ChangeColumn::build($this->table, $columnName, $newColumnType, $options); } - - // if the name was omitted use the existing column name - if ($newColumn->getName() === null || strlen($newColumn->getName()) === 0) { - $newColumn->setName($columnName); - } - - $this->getAdapter()->changeColumn($this->getName(), $columnName, $newColumn); + $this->actions->addAction($action); return $this; } @@ -446,21 +334,10 @@ public function hasColumn($columnName) * @param array $options Index Options * @return \Phinx\Db\Table */ - public function addIndex($columns, $options = []) + public function addIndex($columns, array $options = []) { - // create a new index object if strings or an array of strings were supplied - if (!$columns instanceof Index) { - $index = new Index(); - if (is_string($columns)) { - $columns = [$columns]; // str to array - } - $index->setColumns($columns); - $index->setOptions($options); - } else { - $index = $columns; - } - - $this->indexes[] = $index; + $action = AddIndex::build($this->table, $columns, $options); + $this->actions->addAction($action); return $this; } @@ -471,9 +348,10 @@ public function addIndex($columns, $options = []) * @param array $columns Columns * @return \Phinx\Db\Table */ - public function removeIndex($columns) + public function removeIndex(array $columns) { - $this->getAdapter()->dropIndex($this->getName(), $columns); + $action = DropIndex::build($this->table, $columns); + $this->actions->addAction($action); return $this; } @@ -486,7 +364,8 @@ public function removeIndex($columns) */ public function removeIndexByName($name) { - $this->getAdapter()->dropIndexByName($this->getName(), $name); + $action = DropIndex::buildFromName($this->table, $name); + $this->actions->addAction($action); return $this; } @@ -527,19 +406,36 @@ public function hasIndexByName($indexName) */ public function addForeignKey($columns, $referencedTable, $referencedColumns = ['id'], $options = []) { - if (is_string($referencedColumns)) { - $referencedColumns = [$referencedColumns]; // str to array - } - $fk = new ForeignKey(); - if ($referencedTable instanceof Table) { - $fk->setReferencedTable($referencedTable); - } else { - $fk->setReferencedTable(new Table($referencedTable, [], $this->adapter)); - } - $fk->setColumns($columns) - ->setReferencedColumns($referencedColumns) - ->setOptions($options); - $this->foreignKeys[] = $fk; + $action = AddForeignKey::build($this->table, $columns, $referencedTable, $referencedColumns); + $this->actions->addAction($action); + + return $this; + } + + /** + * Add a foreign key to a database table with a given name. + * + * In $options you can specify on_delete|on_delete = cascade|no_action .., + * on_update, constraint = constraint name. + * + * @param string $name The constaint name + * @param string|array $columns Columns + * @param string|\Phinx\Db\Table $referencedTable Referenced Table + * @param string|array $referencedColumns Referenced Columns + * @param array $options Options + * @return \Phinx\Db\Table + */ + public function addForeignKeyWithName($name, $columns, $referencedTable, $referencedColumns = ['id'], $options = []) + { + $action = AddForeignKey::build( + $this->table, + $columns, + $referencedTable, + $referencedColumns, + $options, + $name + ); + $this->actions->addAction($action); return $this; } @@ -553,14 +449,8 @@ public function addForeignKey($columns, $referencedTable, $referencedColumns = [ */ public function dropForeignKey($columns, $constraint = null) { - if (is_string($columns)) { - $columns = [$columns]; - } - if ($constraint) { - $this->getAdapter()->dropForeignKey($this->getName(), [], $constraint); - } else { - $this->getAdapter()->dropForeignKey($this->getName(), $columns); - } + $action = DropForeignKey::build($this->table, $columns, $constraint); + $this->actions->addAction($action); return $this; } @@ -589,6 +479,7 @@ public function addTimestamps($createdAtColumnName = 'created_at', $updatedAtCol { $createdAtColumnName = is_null($createdAtColumnName) ? 'created_at' : $createdAtColumnName; $updatedAtColumnName = is_null($updatedAtColumnName) ? 'updated_at' : $updatedAtColumnName; + $this->addColumn($createdAtColumnName, 'timestamp', [ 'default' => 'CURRENT_TIMESTAMP', 'update' => '' @@ -635,7 +526,7 @@ public function insert($data) */ public function create() { - $this->getAdapter()->createTable($this); + $this->executeActions(false); $this->saveData(); $this->reset(); // reset pending changes } @@ -648,23 +539,7 @@ public function create() */ public function update() { - if (!$this->exists()) { - throw new \RuntimeException('Cannot update a table that doesn\'t exist!'); - } - - // update table - foreach ($this->getPendingColumns() as $column) { - $this->getAdapter()->addColumn($this, $column); - } - - foreach ($this->getIndexes() as $index) { - $this->getAdapter()->addIndex($this, $index); - } - - foreach ($this->getForeignKeys() as $foreignKey) { - $this->getAdapter()->addForeignKey($this, $foreignKey); - } - + $this->executeActions(true); $this->saveData(); $this->reset(); // reset pending changes } @@ -693,16 +568,16 @@ public function saveData() } if ($bulk) { - $this->getAdapter()->bulkinsert($this, $this->getData()); + $this->getAdapter()->bulkinsert($this->table, $this->getData()); } else { foreach ($this->getData() as $row) { - $this->getAdapter()->insert($this, $row); + $this->getAdapter()->insert($this->table, $row); } } } /** - * Truncates the table. + * Immediately truncates the table. This operation cannot be undone * * @return void */ @@ -728,4 +603,35 @@ public function save() $this->reset(); // reset pending changes } + + /** + * Executes all the pending actions for this table + * + * @param bool $exists Whether or not the table existed prior to executing this method + * @return void + */ + protected function executeActions($exists) + { + // Renaming a table is tricky, specially when running a reversible migration + // down. We will just assume the table already exists if the user commands a + // table rename. + $renamed = collection($this->actions->getActions()) + ->filter(function ($action) { + return $action instanceof RenameTable; + }) + ->first(); + + if ($renamed) { + $exists = true; + } + + // If the table does not exist, the last command in the chain needs to be + // a CreateTable action. + if (!$exists) { + $this->actions->addAction(new CreateTable($this->table)); + } + + $plan = new Plan($this->actions); + $plan->execute($this->getAdapter()); + } } diff --git a/src/Phinx/Db/Table/ForeignKey.php b/src/Phinx/Db/Table/ForeignKey.php index d10db9c5b..14652d4c7 100644 --- a/src/Phinx/Db/Table/ForeignKey.php +++ b/src/Phinx/Db/Table/ForeignKey.php @@ -29,7 +29,7 @@ */ namespace Phinx\Db\Table; -use Phinx\Db\Table; +use Phinx\Db\Table\Table; class ForeignKey { @@ -44,7 +44,7 @@ class ForeignKey protected $columns = []; /** - * @var \Phinx\Db\Table + * @var \Phinx\Db\Table\Table */ protected $referencedTable; @@ -94,7 +94,7 @@ public function getColumns() /** * Sets the foreign key referenced table. * - * @param \Phinx\Db\Table $table + * @param \Phinx\Db\Table\Table $table The table this KEY is pointing to * @return \Phinx\Db\Table\ForeignKey */ public function setReferencedTable(Table $table) @@ -107,7 +107,7 @@ public function setReferencedTable(Table $table) /** * Gets the foreign key referenced table. * - * @return \Phinx\Db\Table + * @return \Phinx\Db\Table\Table */ public function getReferencedTable() { diff --git a/src/Phinx/Db/Table/Index.php b/src/Phinx/Db/Table/Index.php index dd3dcf543..e14367c43 100644 --- a/src/Phinx/Db/Table/Index.php +++ b/src/Phinx/Db/Table/Index.php @@ -56,7 +56,7 @@ class Index protected $type = self::INDEX; /** - * @var string + * @var string|null */ protected $name = null; @@ -127,7 +127,7 @@ public function setName($name) /** * Gets the index name. * - * @return string + * @return string|null */ public function getName() { diff --git a/src/Phinx/Db/Table/Table.php b/src/Phinx/Db/Table/Table.php new file mode 100644 index 000000000..58e073fd5 --- /dev/null +++ b/src/Phinx/Db/Table/Table.php @@ -0,0 +1,75 @@ +name = $name; + $this->options = $options; + } + + /** + * Sets the table name. + * + * @param string $name The name of the table + * @return \Phinx\Db\Table\Table + */ + public function setName($name) + { + $this->name = $name; + + return $this; + } + + /** + * Gets the table name. + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Gets the table options + * + * @return array + */ + public function getOptions() + { + return $this->options; + } + + /** + * Sets the table options + * + * @param array $options The options for the table creation + * @return void + */ + public function setOptions(array $options) + { + $this->options = $options; + } +} diff --git a/src/Phinx/Db/Util/AlterInstructions.php b/src/Phinx/Db/Util/AlterInstructions.php new file mode 100644 index 000000000..19687fdac --- /dev/null +++ b/src/Phinx/Db/Util/AlterInstructions.php @@ -0,0 +1,140 @@ +alterParts = $alterParts; + $this->postSteps = $postSteps; + } + + /** + * Adds another parst for the single ALTER instruction + * + * @param string $part The SQL snipped to add as part of the ALTER instruction + * @return void + */ + public function addAlter($part) + { + $this->alterParts[] = $part; + } + + /** + * Adds a SQL command to be eecuted after the ALTER instruction. + * This method allows a callable, with will get an empty array as state + * for the first time and will pass the return value of the callable to + * the next callable, if present. + * + * This allows to keep a single state across callbacks. + * + * @param string|callable $sql The SQL to run after, or a callable to execute + * @return void + */ + public function addPostStep($sql) + { + $this->postSteps[] = $sql; + } + + /** + * Returns the alter SQL snippets + * + * @return string[] + */ + public function getAlterParts() + { + return $this->alterParts; + } + + /** + * Returns the SQL commands to run after the ALTER instrunction + * + * @return mixed[] + */ + public function getPostSteps() + { + return $this->postSteps; + } + + /** + * Merges another AlterInstructions object to this one + * + * @param AlterInstructions $other The other collection of instructions to merge in + * @return void + */ + public function merge(AlterInstructions $other) + { + $this->alterParts = array_merge($this->alterParts, $other->getAlterParts()); + $this->postSteps = array_merge($this->postSteps, $other->getPostSteps()); + } + + /** + * Executes the ALTER instruction and all of the post steps. + * + * @param string $alterTemplate The template for the alter instruction + * @param callable $executor The function to be used to execute all instructions + * @return void + */ + public function execute($alterTemplate, callable $executor) + { + if ($this->alterParts) { + $alter = sprintf($alterTemplate, implode(', ', $this->alterParts)); + $executor($alter); + } + + $state = []; + + foreach ($this->postSteps as $instruction) { + if (is_callable($instruction)) { + $state = $instruction($state); + continue; + } + + $executor($instruction); + } + } +} diff --git a/src/Phinx/Migration/Migration.template.php.dist b/src/Phinx/Migration/Migration.template.php.dist index 406ebae56..962c4b497 100644 --- a/src/Phinx/Migration/Migration.template.php.dist +++ b/src/Phinx/Migration/Migration.template.php.dist @@ -24,8 +24,8 @@ class $className extends $baseClassName * addIndex * addForeignKey * - * Remember to call "create()" or "update()" and NOT "save()" when working - * with the Table class. + * Any other distructive changes will result in an error when trying to + * rollback the migration. */ public function change() { diff --git a/tests/Phinx/Console/Command/CreateTest.php b/tests/Phinx/Console/Command/CreateTest.php index 733bd9476..fbc17d595 100644 --- a/tests/Phinx/Console/Command/CreateTest.php +++ b/tests/Phinx/Console/Command/CreateTest.php @@ -304,7 +304,8 @@ public function testNullTemplateGeneratorsDoNotFail(array $config, array $comman $commandTester = new CommandTester($command); $commandLine = array_merge(['command' => $command->getName()], $commandLine); - $commandTester->execute($commandLine); + $res = $commandTester->execute($commandLine); + $this->assertEquals(0, $res); } public function provideSimpleTemplateGenerator() diff --git a/tests/Phinx/Db/Adapter/MysqlAdapterTest.php b/tests/Phinx/Db/Adapter/MysqlAdapterTest.php index f77527dc0..9bb610661 100644 --- a/tests/Phinx/Db/Adapter/MysqlAdapterTest.php +++ b/tests/Phinx/Db/Adapter/MysqlAdapterTest.php @@ -209,12 +209,6 @@ public function testCreateTableCustomIdColumn() $this->assertFalse($this->adapter->hasColumn('ntable', 'address')); } - public function testCreateTableWithNoOptions() - { - $this->markTestIncomplete(); - //$this->adapter->createTable('ntable', ) - } - public function testCreateTableWithNoPrimaryKey() { $options = [ @@ -382,7 +376,8 @@ public function testRenameTable() $table->save(); $this->assertTrue($this->adapter->hasTable('table1')); $this->assertFalse($this->adapter->hasTable('table2')); - $this->adapter->renameTable('table1', 'table2'); + + $table->rename('table2')->save(); $this->assertFalse($this->adapter->hasTable('table1')); $this->assertTrue($this->adapter->hasTable('table2')); } @@ -518,7 +513,8 @@ public function testRenameColumn() ->save(); $this->assertTrue($this->adapter->hasColumn('t', 'column1')); $this->assertFalse($this->adapter->hasColumn('t', 'column2')); - $this->adapter->renameColumn('t', 'column1', 'column2'); + + $table->renameColumn('column1', 'column2')->save(); $this->assertFalse($this->adapter->hasColumn('t', 'column1')); $this->assertTrue($this->adapter->hasColumn('t', 'column2')); } @@ -530,7 +526,7 @@ public function testRenamingANonExistentColumn() ->save(); try { - $this->adapter->renameColumn('t', 'column2', 'column1'); + $table->renameColumn('column2', 'column1')->save(); $this->fail('Expected the adapter to throw an exception'); } catch (\InvalidArgumentException $e) { $this->assertInstanceOf( @@ -548,14 +544,13 @@ public function testChangeColumn() $table->addColumn('column1', 'string') ->save(); $this->assertTrue($this->adapter->hasColumn('t', 'column1')); - $newColumn1 = new \Phinx\Db\Table\Column(); - $newColumn1->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', 'string')->save(); $this->assertTrue($this->adapter->hasColumn('t', 'column1')); + $newColumn2 = new \Phinx\Db\Table\Column(); $newColumn2->setName('column2') ->setType('string'); - $table->changeColumn('column1', $newColumn2); + $table->changeColumn('column1', $newColumn2)->save(); $this->assertFalse($this->adapter->hasColumn('t', 'column1')); $this->assertTrue($this->adapter->hasColumn('t', 'column2')); } @@ -568,7 +563,7 @@ public function testChangeColumnDefaultValue() $newColumn1 = new \Phinx\Db\Table\Column(); $newColumn1->setDefault('test1') ->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $rows = $this->adapter->fetchAll('SHOW COLUMNS FROM t'); $this->assertNotNull($rows[1]['Default']); $this->assertEquals("test1", $rows[1]['Default']); @@ -582,7 +577,7 @@ public function testChangeColumnDefaultToZero() $newColumn1 = new \Phinx\Db\Table\Column(); $newColumn1->setDefault(0) ->setType('integer'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $rows = $this->adapter->fetchAll('SHOW COLUMNS FROM t'); $this->assertNotNull($rows[1]['Default']); $this->assertEquals("0", $rows[1]['Default']); @@ -596,7 +591,7 @@ public function testChangeColumnDefaultToNull() $newColumn1 = new \Phinx\Db\Table\Column(); $newColumn1->setDefault(null) ->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $rows = $this->adapter->fetchAll('SHOW COLUMNS FROM t'); $this->assertNull($rows[1]['Default']); } @@ -756,42 +751,52 @@ public function testDropColumn() $table->addColumn('column1', 'string') ->save(); $this->assertTrue($this->adapter->hasColumn('t', 'column1')); - $this->adapter->dropColumn('t', 'column1'); + + $table->removeColumn('column1')->save(); $this->assertFalse($this->adapter->hasColumn('t', 'column1')); } - public function testGetColumns() + public function columnsProvider() + { + return [ + ['column1', 'string', []], + ['column2', 'integer', []], + ['column3', 'biginteger', []], + ['column4', 'text', []], + ['column5', 'float', []], + ['column6', 'decimal', []], + ['column7', 'datetime', []], + ['column8', 'time', []], + ['column9', 'timestamp', []], + ['column10', 'date', []], + ['column11', 'binary', []], + ['column12', 'boolean', []], + ['column13', 'string', ['limit' => 10]], + ['column15', 'integer', ['limit' => 10]], + ['column16', 'geometry', []], + ['column17', 'point', []], + ['column18', 'linestring', []], + ['column19', 'polygon', []], + ['column20', 'uuid', []], + ['column21', 'set', ['values' => "one, two"]], + ['column22', 'enum', ['values' => ['three', 'four']]], + ['column23', 'bit', []] + ]; + } + + /** + * + * @dataProvider columnsProvider + */ + public function testGetColumns($colName, $type, $options) { $table = new \Phinx\Db\Table('t', [], $this->adapter); - $table->addColumn('column1', 'string') - ->addColumn('column2', 'integer') - ->addColumn('column3', 'biginteger') - ->addColumn('column4', 'text') - ->addColumn('column5', 'float') - ->addColumn('column6', 'decimal') - ->addColumn('column7', 'datetime') - ->addColumn('column8', 'time') - ->addColumn('column9', 'timestamp') - ->addColumn('column10', 'date') - ->addColumn('column11', 'binary') - ->addColumn('column12', 'boolean') - ->addColumn('column13', 'string', ['limit' => 10]) - ->addColumn('column15', 'integer', ['limit' => 10]) - ->addColumn('column16', 'geometry') - ->addColumn('column17', 'point') - ->addColumn('column18', 'linestring') - ->addColumn('column19', 'polygon') - ->addColumn('column20', 'uuid') - ->addColumn('column21', 'set', ['values' => "one, two"]) - ->addColumn('column22', 'enum', ['values' => ['three', 'four']]) - ->addColumn('column23', 'bit'); - $pendingColumns = $table->getPendingColumns(); - $table->save(); + $table->addColumn($colName, $type, $options)->save(); + $columns = $this->adapter->getColumns('t'); - $this->assertCount(count($pendingColumns) + 1, $columns); - for ($i = 0; $i++; $i < count($pendingColumns)) { - $this->assertEquals($pendingColumns[$i], $columns[$i + 1]); - } + $this->assertCount(2, $columns); + $this->assertEquals($colName, $columns[1]->getName()); + $this->assertEquals($type, $columns[1]->getType()); } public function testDescribeTable() @@ -811,35 +816,9 @@ public function testDescribeTable() public function testGetColumnsReservedTableName() { $table = new \Phinx\Db\Table('group', [], $this->adapter); - $table->addColumn('column1', 'string') - ->addColumn('column2', 'integer') - ->addColumn('column3', 'biginteger') - ->addColumn('column4', 'text') - ->addColumn('column5', 'float') - ->addColumn('column6', 'decimal') - ->addColumn('column7', 'datetime') - ->addColumn('column8', 'time') - ->addColumn('column9', 'timestamp') - ->addColumn('column10', 'date') - ->addColumn('column11', 'binary') - ->addColumn('column12', 'boolean') - ->addColumn('column13', 'string', ['limit' => 10]) - ->addColumn('column15', 'integer', ['limit' => 10]) - ->addColumn('column16', 'geometry') - ->addColumn('column17', 'point') - ->addColumn('column18', 'linestring') - ->addColumn('column19', 'polygon') - ->addColumn('column20', 'uuid') - ->addColumn('column21', 'set', ['values' => "one, two"]) - ->addColumn('column22', 'enum', ['values' => ['three', 'four']]) - ->addColumn('column23', 'bit'); - $pendingColumns = $table->getPendingColumns(); - $table->save(); + $table->addColumn('column1', 'string')->save(); $columns = $this->adapter->getColumns('group'); - $this->assertCount(count($pendingColumns) + 1, $columns); - for ($i = 0; $i++; $i < count($pendingColumns)) { - $this->assertEquals($pendingColumns[$i], $columns[$i + 1]); - } + $this->assertCount(2, $columns); } public function testAddIndex() @@ -878,7 +857,7 @@ public function testDropIndex() ->addIndex('email') ->save(); $this->assertTrue($table->hasIndex('email')); - $this->adapter->dropIndex($table->getName(), 'email'); + $table->removeIndex(['email'])->save(); $this->assertFalse($table->hasIndex('email')); // multiple column index @@ -888,7 +867,7 @@ public function testDropIndex() ->addIndex(['fname', 'lname']) ->save(); $this->assertTrue($table2->hasIndex(['fname', 'lname'])); - $this->adapter->dropIndex($table2->getName(), ['fname', 'lname']); + $table2->removeIndex(['fname', 'lname'])->save(); $this->assertFalse($table2->hasIndex(['fname', 'lname'])); // index with name specified, but dropping it by column name @@ -897,7 +876,7 @@ public function testDropIndex() ->addIndex('email', ['name' => 'someindexname']) ->save(); $this->assertTrue($table3->hasIndex('email')); - $this->adapter->dropIndex($table3->getName(), 'email'); + $table3->removeIndex(['email'])->save(); $this->assertFalse($table3->hasIndex('email')); // multiple column index with name specified @@ -907,7 +886,7 @@ public function testDropIndex() ->addIndex(['fname', 'lname'], ['name' => 'multiname']) ->save(); $this->assertTrue($table4->hasIndex(['fname', 'lname'])); - $this->adapter->dropIndex($table4->getName(), ['fname', 'lname']); + $table4->removeIndex(['fname', 'lname'])->save(); $this->assertFalse($table4->hasIndex(['fname', 'lname'])); // don't drop multiple column index when dropping single column @@ -917,7 +896,11 @@ public function testDropIndex() ->addIndex(['fname', 'lname']) ->save(); $this->assertTrue($table2->hasIndex(['fname', 'lname'])); - $this->adapter->dropIndex($table2->getName(), ['fname']); + + try { + $table2->removeIndex(['fname'])->save(); + } catch (\InvalidArgumentException $e) { + } $this->assertTrue($table2->hasIndex(['fname', 'lname'])); // don't drop multiple column index with name specified when dropping @@ -928,7 +911,12 @@ public function testDropIndex() ->addIndex(['fname', 'lname'], ['name' => 'multiname']) ->save(); $this->assertTrue($table4->hasIndex(['fname', 'lname'])); - $this->adapter->dropIndex($table4->getName(), ['fname']); + + try { + $table4->removeIndex(['fname'])->save(); + } catch (\InvalidArgumentException $e) { + } + $this->assertTrue($table4->hasIndex(['fname', 'lname'])); } @@ -940,7 +928,7 @@ public function testDropIndexByName() ->addIndex('email', ['name' => 'myemailindex']) ->save(); $this->assertTrue($table->hasIndex('email')); - $this->adapter->dropIndexByName($table->getName(), 'myemailindex'); + $table->removeIndexByName('myemailindex')->save(); $this->assertFalse($table->hasIndex('email')); // multiple column index @@ -950,7 +938,7 @@ public function testDropIndexByName() ->addIndex(['fname', 'lname'], ['name' => 'twocolumnindex']) ->save(); $this->assertTrue($table2->hasIndex(['fname', 'lname'])); - $this->adapter->dropIndexByName($table2->getName(), 'twocolumnindex'); + $table2->removeIndexByName('twocolumnindex')->save(); $this->assertFalse($table2->hasIndex(['fname', 'lname'])); } @@ -960,14 +948,11 @@ public function testAddForeignKey() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } @@ -977,14 +962,11 @@ public function testAddForeignKeyForTableWithUnsignedPK() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer', ['signed' => false])->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer', ['signed' => false]) + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } @@ -994,15 +976,12 @@ public function testDropForeignKey() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); - $this->adapter->dropForeignKey($table->getName(), ['ref_table_id']); + $table->dropForeignKey(['ref_table_id'])->save(); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } @@ -1012,15 +991,12 @@ public function testDropForeignKeyForTableWithUnsignedPK() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer', ['signed' => false])->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer', ['signed' => false]) + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); - $this->adapter->dropForeignKey($table->getName(), ['ref_table_id']); + $table->dropForeignKey(['ref_table_id'])->save(); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } @@ -1030,68 +1006,26 @@ public function testDropForeignKeyAsString() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); - $this->adapter->dropForeignKey($table->getName(), 'ref_table_id'); + $table->dropForeignKey('ref_table_id')->save(); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } - public function testHasForeignKey() - { - $refTable = new \Phinx\Db\Table('ref_table', [], $this->adapter); - $refTable->addColumn('field1', 'string')->save(); - - $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); - - $this->adapter->addForeignKey($table, $fk); - $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); - $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id2'])); - } - - public function testHasForeignKeyForTableWithUnsignedPK() - { - $refTable = new \Phinx\Db\Table('ref_table', ['signed' => false], $this->adapter); - $refTable->addColumn('field1', 'string')->save(); - - $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer', ['signed' => false])->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); - - $this->adapter->addForeignKey($table, $fk); - $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); - $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id2'])); - } - public function testHasForeignKeyAsString() { $refTable = new \Phinx\Db\Table('ref_table', [], $this->adapter); $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), 'ref_table_id')); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), 'ref_table_id2')); } @@ -1102,15 +1036,11 @@ public function testHasForeignKeyWithConstraint() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setConstraint("my_constraint") - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKeyWithName('my_constraint', ['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'], 'my_constraint')); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'], 'my_constraint2')); } @@ -1121,15 +1051,11 @@ public function testHasForeignKeyWithConstraintForTableWithUnsignedPK() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer', ['signed' => false])->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setConstraint("my_constraint") - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer', ['signed' => false]) + ->addForeignKeyWithName('my_constraint', ['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'], 'my_constraint')); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'], 'my_constraint2')); } @@ -1251,10 +1177,8 @@ public function testBulkInsertData() $table->addColumn('column1', 'string') ->addColumn('column2', 'integer') ->addColumn('column3', 'string', ['default' => 'test']) - ->insert($data); - $this->adapter->createTable($table); - $this->adapter->bulkinsert($table, $table->getData()); - $table->reset(); + ->insert($data) + ->save(); $rows = $this->adapter->fetchAll('SELECT * FROM table1'); $this->assertEquals('value1', $rows[0]['column1']); @@ -1342,15 +1266,15 @@ public function testDumpInsert() $consoleOutput = new BufferedOutput(); $this->adapter->setOutput($consoleOutput); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'string_col' => 'test data' ]); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'string_col' => null ]); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'int_col' => 23 ]); @@ -1386,7 +1310,7 @@ public function testDumpBulkinsert() $consoleOutput = new BufferedOutput(); $this->adapter->setOutput($consoleOutput); - $this->adapter->bulkinsert($table, [ + $this->adapter->bulkinsert($table->getTable(), [ [ 'string_col' => 'test_data1', 'int_col' => 23, diff --git a/tests/Phinx/Db/Adapter/MysqlAdapterUnitTest.php b/tests/Phinx/Db/Adapter/MysqlAdapterUnitTest.php deleted file mode 100644 index a474ce5a0..000000000 --- a/tests/Phinx/Db/Adapter/MysqlAdapterUnitTest.php +++ /dev/null @@ -1,1798 +0,0 @@ -connection = $connection; - } - - public function getConnection() - { - return $this->connection; - } - - // change visibility for testing - public function getDefaultValueDefinition($default, $type = null) - { - return parent::getDefaultValueDefinition($default, $type); - } - - public function getColumnSqlDefinition(Column $column) - { - return parent::getColumnSqlDefinition($column); - } - - public function getIndexSqlDefinition(Index $index) - { - return parent::getIndexSqlDefinition($index); - } - - public function getIndexes($tableName) - { - return parent::getIndexes($tableName); - } - - public function getForeignKeys($tableName) - { - return parent::getForeignKeys($tableName); - } -} - -class MysqlAdapterUnitTest extends TestCase -{ - /** - * @var MysqlAdapterTester - */ - private $adapter; - - private $conn; - - private $result; - - public function setUp() - { - if (!TESTS_PHINX_DB_ADAPTER_MYSQL_ENABLED) { - $this->markTestSkipped('Mysql tests disabled. See TESTS_PHINX_DB_ADAPTER_MYSQL_ENABLED constant.'); - } - - $this->adapter = new MysqlAdapterTester([], new ArrayInput([]), new NullOutput()); - - $this->conn = $this->getMockBuilder('PDOMock') - ->disableOriginalConstructor() - ->setMethods([ 'query', 'exec', 'quote' ]) - ->getMock(); - $this->result = $this->getMockBuilder('stdclass') - ->disableOriginalConstructor() - ->setMethods([ 'fetch' ]) - ->getMock(); - $this->adapter->setMockConnection($this->conn); - } - - // helper methods for easy mocking - private function assertExecuteSql($expected_sql) - { - $this->conn->expects($this->once()) - ->method('exec') - ->with($this->equalTo($expected_sql)); - } - - private function assertQuerySql($expectedSql, $returnValue = null) - { - $expect = $this->conn->expects($this->once()) - ->method('query') - ->with($this->equalTo($expectedSql)); - if (!is_null($returnValue)) { - $expect->will($this->returnValue($returnValue)); - } - } - - private function assertFetchRowSql($expectedSql, $returnValue) - { - $this->result->expects($this->once()) - ->method('fetch') - ->will($this->returnValue($returnValue)); - $this->assertQuerySql($expectedSql, $this->result); - } - - public function testDisconnect() - { - $this->assertNotNull($this->adapter->getConnection()); - $this->adapter->disconnect(); - $this->assertNull($this->adapter->getConnection()); - } - - // database related tests - - public function testHasDatabaseExists() - { - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue(['SCHEMA_NAME' => 'database_name'])); - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'database_name'", $this->result); - - $this->assertTrue($this->adapter->hasDatabase('database_name')); - } - - public function testHasDatabaseNotExists() - { - $this->result->expects($this->once()) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql("SELECT SCHEMA_NAME FROM INFORMATION_SCHEMA.SCHEMATA WHERE SCHEMA_NAME = 'database_name2'", $this->result); - - $this->assertFalse($this->adapter->hasDatabase('database_name2')); - } - - public function testDropDatabase() - { - $this->assertExecuteSql("DROP DATABASE IF EXISTS `database_name`"); - $this->adapter->dropDatabase('database_name'); - } - - public function testCreateDatabase() - { - $this->assertExecuteSql("CREATE DATABASE `database_name` DEFAULT CHARACTER SET `utf8`"); - $this->adapter->createDatabase('database_name'); - } - - public function testCreateDatabaseWithCharset() - { - $this->assertExecuteSql("CREATE DATABASE `database_name` DEFAULT CHARACTER SET `latin1`"); - $this->adapter->createDatabase('database_name', ['charset' => 'latin1']); - } - - public function testCreateDatabaseWithCharsetAndCollation() - { - $this->assertExecuteSql("CREATE DATABASE `database_name` DEFAULT CHARACTER SET `latin1` COLLATE `latin1_swedish_ci`"); - $this->adapter->createDatabase('database_name', ['charset' => 'latin1', 'collation' => 'latin1_swedish_ci']); - } - - public function testHasTransactions() - { - $this->assertTrue($this->adapter->hasTransactions()); - } - - public function testBeginTransaction() - { - $this->assertExecuteSql("START TRANSACTION"); - $this->adapter->beginTransaction(); - } - - public function testCommitTransaction() - { - $this->assertExecuteSql("COMMIT"); - $this->adapter->commitTransaction(); - } - - public function testRollbackTransaction() - { - $this->assertExecuteSql("ROLLBACK"); - $this->adapter->rollbackTransaction(); - } - - // table related tests - - public function testDescribeTable() - { - $this->adapter->setOptions(['name' => 'database_name']); - - $expectedSql = "SELECT * - FROM information_schema.tables - WHERE table_schema = 'database_name' - AND table_name = 'table_name'"; - - $returnValue = ['TABLE_TYPE' => 'BASE_TABLE', - 'TABLE_NAME' => 'table_name', - 'TABLE_SCHEMA' => 'database_name', - 'TABLE_ROWS' => 0]; - $this->assertFetchRowSql($expectedSql, $returnValue); - - $described = $this->adapter->describeTable('table_name'); - $this->assertEquals($returnValue, $described); - } - - public function testRenameTable() - { - $this->assertExecuteSql("RENAME TABLE `old_table_name` TO `new_table_name`"); - $this->adapter->renameTable('old_table_name', 'new_table_name'); - } - - public function testDropTable() - { - $this->assertExecuteSql("DROP TABLE `table_name`"); - $this->adapter->dropTable("table_name"); - } - - public function testTruncateTable() - { - $this->assertExecuteSql("TRUNCATE TABLE `table_name`"); - $this->adapter->truncateTable("table_name"); - } - - public function testHasTableExists() - { - $this->adapter->setOptions(['name' => 'database_name']); - $this->result->expects($this->once()) - ->method('fetch') - ->will($this->returnValue(['somecontent'])); - $expectedSql = 'SELECT TABLE_NAME - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = \'database_name\' AND TABLE_NAME = \'table_name\''; - $this->assertQuerySql($expectedSql, $this->result); - $this->assertTrue($this->adapter->hasTable("table_name")); - } - - public function testHasTableNotExists() - { - $this->adapter->setOptions(['name' => 'database_name']); - $this->result->expects($this->once()) - ->method('fetch') - ->will($this->returnValue([])); - $expectedSql = 'SELECT TABLE_NAME - FROM INFORMATION_SCHEMA.TABLES - WHERE TABLE_SCHEMA = \'database_name\' AND TABLE_NAME = \'table_name\''; - $this->assertQuerySql($expectedSql, $this->result); - $this->assertFalse($this->adapter->hasTable("table_name")); - } - - public function testCreateTableBasic() - { - $column1 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column1->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column1->expects($this->any())->method('getType')->will($this->returnValue('string')); - $column1->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column1->expects($this->at(0))->method('getLimit')->will($this->returnValue('64')); - - $column2 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column2->expects($this->any())->method('getName')->will($this->returnValue('column_name2')); - $column2->expects($this->any())->method('getType')->will($this->returnValue('integer')); - $column2->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column2->expects($this->at(0))->method('getLimit')->will($this->returnValue('4')); - - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName', 'getOptions', 'getPendingColumns', 'getIndexes', 'getForeignKeys']) - ->getMock(); - - $table->expects($this->any())->method('getPendingColumns')->will($this->returnValue([$column1, $column2])); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - $table->expects($this->any())->method('getOptions')->will($this->returnValue([])); - $table->expects($this->any())->method('getIndexes')->will($this->returnValue([])); - $table->expects($this->any())->method('getForeignKeys')->will($this->returnValue([])); - - $expectedSql = 'CREATE TABLE `table_name` (`id` INT(11) NOT NULL AUTO_INCREMENT, `column_name` VARCHAR(255) NOT NULL, `column_name2` INT(11) NOT NULL, PRIMARY KEY (`id`)) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;'; - $this->assertExecuteSql($expectedSql); - $this->adapter->createTable($table); - } - - public function testCreateTablePrimaryKey() - { - $column1 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column1->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column1->expects($this->any())->method('getType')->will($this->returnValue('string')); - $column1->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column1->expects($this->at(0))->method('getLimit')->will($this->returnValue('64')); - - $column2 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column2->expects($this->any())->method('getName')->will($this->returnValue('column_name2')); - $column2->expects($this->any())->method('getType')->will($this->returnValue('integer')); - $column2->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column2->expects($this->at(0))->method('getLimit')->will($this->returnValue('4')); - - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName', 'getOptions', 'getPendingColumns', 'getIndexes', 'getForeignKeys']) - ->getMock(); - - $tableOptions = ['id' => 'column_name2']; - $table->expects($this->any())->method('getPendingColumns')->will($this->returnValue([$column1, $column2])); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - $table->expects($this->any())->method('getOptions')->will($this->returnValue($tableOptions)); - $table->expects($this->any())->method('getIndexes')->will($this->returnValue([])); - $table->expects($this->any())->method('getForeignKeys')->will($this->returnValue([])); - - $expectedSql = 'CREATE TABLE `table_name` (`column_name2` INT(11) NOT NULL AUTO_INCREMENT, `column_name` VARCHAR(255) NOT NULL, `column_name2` INT(11) NOT NULL, PRIMARY KEY (`column_name2`)) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;'; - $this->assertExecuteSql($expectedSql); - $this->adapter->createTable($table); - } - - public function testCreateTableUnsignedPK() - { - $column1 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column1->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column1->expects($this->any())->method('getType')->will($this->returnValue('string')); - $column1->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column1->expects($this->at(0))->method('getLimit')->will($this->returnValue('64')); - - $column2 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column2->expects($this->any())->method('getName')->will($this->returnValue('column_name2')); - $column2->expects($this->any())->method('getType')->will($this->returnValue('integer')); - $column2->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column2->expects($this->at(0))->method('getLimit')->will($this->returnValue('4')); - - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName', 'getOptions', 'getPendingColumns', 'getIndexes', 'getForeignKeys']) - ->getMock(); - - $tableOptions = ['signed' => false]; - $table->expects($this->any())->method('getPendingColumns')->will($this->returnValue([$column1, $column2])); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - $table->expects($this->any())->method('getOptions')->will($this->returnValue($tableOptions)); - $table->expects($this->any())->method('getIndexes')->will($this->returnValue([])); - $table->expects($this->any())->method('getForeignKeys')->will($this->returnValue([])); - - $expectedSql = 'CREATE TABLE `table_name` (`id` INT(11) unsigned NOT NULL AUTO_INCREMENT, `column_name` VARCHAR(255) NOT NULL, `column_name2` INT(11) NOT NULL, PRIMARY KEY (`id`)) ENGINE = InnoDB CHARACTER SET utf8 COLLATE utf8_general_ci;'; - $this->assertExecuteSql($expectedSql); - $this->adapter->createTable($table); - } - - public function testCreateTableAdvanced() - { - $refTable = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName', 'getOptions', 'getPendingColumns', 'getIndexes', 'getForeignKeys']) - ->getMock(); - $refTable->expects($this->any())->method('getName')->will($this->returnValue('other_table')); - - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName', 'getOptions', 'getPendingColumns', 'getIndexes', 'getForeignKeys']) - ->getMock(); - - $tableOptions = ['collation' => 'latin1_swedish_ci', - 'engine' => 'MyISAM', - 'id' => ['ref_id', 'other_table_id'], - 'primary_key' => ['ref_id', 'other_table_id'], - 'comment' => "Table Comment"]; - $this->conn->expects($this->any())->method('quote')->with('Table Comment')->will($this->returnValue('`Table Comment`')); - - $column1 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column1->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column1->expects($this->any())->method('getType')->will($this->returnValue('string')); - $column1->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column1->expects($this->at(0))->method('getLimit')->will($this->returnValue('64')); - - $column2 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column2->expects($this->any())->method('getName')->will($this->returnValue('other_table_id')); - $column2->expects($this->any())->method('getType')->will($this->returnValue('integer')); - $column2->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column2->expects($this->at(0))->method('getLimit')->will($this->returnValue('4')); - - $column3 = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column3->expects($this->any())->method('getName')->will($this->returnValue('ref_id')); - $column3->expects($this->any())->method('getType')->will($this->returnValue('integer')); - $column3->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column3->expects($this->at(0))->method('getLimit')->will($this->returnValue('11')); - - $index = $this->getMockBuilder('Phinx\Db\Table\Index') - ->disableOriginalConstructor() - ->setMethods([ 'getColumns']) - ->getMock(); - - $index->expects($this->any())->method('getColumns')->will($this->returnValue(['column_name'])); - - $foreignkey = $this->getMockBuilder('Phinx\Db\Table\ForeignKey') - ->disableOriginalConstructor() - ->setMethods([ 'getColumns', - 'getConstraint', - 'getReferencedColumns', - 'getOnDelete', - 'getOnUpdate', - 'getReferencedTable']) - ->getMock(); - - $foreignkey->expects($this->any())->method('getColumns')->will($this->returnValue(['other_table_id'])); - $foreignkey->expects($this->any())->method('getConstraint')->will($this->returnValue('fk1')); - $foreignkey->expects($this->any())->method('getReferencedColumns')->will($this->returnValue(['id'])); - $foreignkey->expects($this->any())->method('getReferencedTable')->will($this->returnValue($refTable)); - $foreignkey->expects($this->any())->method('getOnDelete')->will($this->returnValue(null)); - $foreignkey->expects($this->any())->method('getOnUpdate')->will($this->returnValue(null)); - - $table->expects($this->any())->method('getPendingColumns')->will($this->returnValue([$column1, $column2, $column3])); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - $table->expects($this->any())->method('getOptions')->will($this->returnValue($tableOptions)); - $table->expects($this->any())->method('getIndexes')->will($this->returnValue([$index])); - $table->expects($this->any())->method('getForeignKeys')->will($this->returnValue([$foreignkey])); - - $expectedSql = 'CREATE TABLE `table_name` (`column_name` VARCHAR(255) NOT NULL, `other_table_id` INT(11) NOT NULL, `ref_id` INT(11) NOT NULL, PRIMARY KEY (`ref_id`,`other_table_id`), KEY (`column_name`), CONSTRAINT `fk1` FOREIGN KEY (`other_table_id`) REFERENCES `other_table` (`id`)) ENGINE = MyISAM CHARACTER SET latin1 COLLATE latin1_swedish_ci COMMENT=`Table Comment`;'; - $this->assertExecuteSql($expectedSql); - $this->adapter->createTable($table); - } - - /** - * @todo not real unit, Column class is not mocked, improve dependency of Column removing new. Could be done calling protected newColumn() and override newColumn() in tester class - * - */ - public function testGetColumns() - { - $column1 = [ - 'Field' => 'column1', - 'Type' => 'int(15)', - 'Null' => 'NO', - 'Default' => '', - 'Key' => 'PRI', - 'Extra' => 'auto_increment' - ]; - - $column2 = [ - 'Field' => 'column2', - 'Type' => 'varchar(32)', - 'Null' => '', - 'Default' => 'NULL', - 'Key' => '', - 'Extra' => '' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($column1)); - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($column2)); - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql("SHOW COLUMNS FROM `table_name`", $this->result); - - $columns = $this->adapter->getColumns("table_name"); - - $this->assertInternalType('array', $columns); - $this->assertCount(2, $columns); - - $this->assertEquals('column1', $columns[0]->getName()); - $this->assertInstanceOf('Phinx\Db\Table\Column', $columns[0]); - $this->assertEquals('15', $columns[0]->getLimit()); - $this->assertFalse($columns[0]->getNull()); - $this->assertEquals('', $columns[0]->getDefault()); - $this->assertTrue($columns[0]->getIdentity()); - - $this->assertEquals('column2', $columns[1]->getName()); - $this->assertInstanceOf('Phinx\Db\Table\Column', $columns[1]); - $this->assertEquals('32', $columns[1]->getLimit()); - $this->assertTrue($columns[1]->getNull()); - $this->assertEquals('NULL', $columns[1]->getDefault()); - $this->assertFalse($columns[1]->getIdentity()); - } - - // column related tests - - public function testHasColumnExists() - { - $column1 = [ - 'Field' => 'column1', - 'Type' => 'int(15)', - 'Null' => 'NO', - 'Default' => '', - 'Extra' => 'auto_increment' - ]; - - $column2 = [ - 'Field' => 'column2', - 'Type' => 'varchar(32)', - 'Null' => '', - 'Default' => 'NULL', - 'Extra' => '' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($column1)); - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($column2)); - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql('SHOW COLUMNS FROM `table_name`', $this->result); - - $this->assertTrue($this->adapter->hasColumn('table_name', 'column1')); - } - - public function testGetColumnSqlDefinitionInteger() - { - $column = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit']) - ->getMock(); - - $column->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column->expects($this->any())->method('getLimit')->will($this->returnValue('11')); - $column->expects($this->any())->method('getType')->will($this->returnValue('integer')); - - $this->assertEquals( - "INT(11) NOT NULL", - $this->adapter->getColumnSqlDefinition($column) - ); - } - - public function testGetColumnSqlDefinitionFloat() - { - $column = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit', 'getScale', 'getPrecision']) - ->getMock(); - - $column->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column->expects($this->any())->method('getType')->will($this->returnValue('float')); - $column->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column->expects($this->any())->method('getPrecision')->will($this->returnValue('8')); - $column->expects($this->any())->method('getScale')->will($this->returnValue('3')); - - $this->assertEquals( - "FLOAT(8,3) NOT NULL", - $this->adapter->getColumnSqlDefinition($column) - ); - } - - /** - * @todo must enter in code that removes limit - */ - public function testGetColumnSqlDefinitionTextWithLimit() - { - $column = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit', 'setLimit']) - ->getMock(); - - $column->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column->expects($this->any())->method('getType')->will($this->returnValue('text')); - $column->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column->expects($this->at(0))->method('getLimit')->will($this->returnValue('2048')); - $column->expects($this->at(1))->method('getLimit')->will($this->returnValue(null)); - - $this->assertEquals( - "TEXT NOT NULL", - $this->adapter->getColumnSqlDefinition($column) - ); - } - - public function testGetColumnSqlDefinitionComplete() - { - $this->conn->expects($this->once()) - ->method('quote') - ->with($this->equalTo('Custom Comment')) - ->will($this->returnValue("`Custom Comment`")); - - $column = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', - 'getAfter', - 'getType', - 'getLimit', - 'getScale', - 'getPrecision', - 'getComment', - 'isIdentity', - 'getUpdate']) - ->getMock(); - - $column->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column->expects($this->any())->method('isIdentity')->will($this->returnValue(true)); - $column->expects($this->any())->method('getComment')->will($this->returnValue('Custom Comment')); - $column->expects($this->any())->method('getUpdate')->will($this->returnValue('CASCADE')); - $column->expects($this->any())->method('getLimit')->will($this->returnValue('')); - $column->expects($this->any())->method('getScale')->will($this->returnValue('2')); - $column->expects($this->any())->method('getPrecision')->will($this->returnValue('8')); - $column->expects($this->any())->method('getType')->will($this->returnValue('float')); - - $this->assertEquals( - "FLOAT(8,2) NOT NULL AUTO_INCREMENT COMMENT `Custom Comment` ON UPDATE CASCADE", - $this->adapter->getColumnSqlDefinition($column) - ); - } - - public function testHasColumnExistsCaseInsensitive() - { - $column1 = [ - 'Field' => 'column1', - 'Type' => 'int(15)', - 'Null' => 'NO', - 'Default' => '', - 'Extra' => 'auto_increment' - ]; - - $column2 = [ - 'Field' => 'column2', - 'Type' => 'varchar(32)', - 'Null' => '', - 'Default' => 'NULL', - 'Extra' => '' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($column1)); - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($column2)); - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql('SHOW COLUMNS FROM `table_name`', $this->result); - - $this->assertTrue($this->adapter->hasColumn('table_name', 'CoLumN1')); - } - - public function testHasColumnNotExists() - { - $column1 = [ - 'Field' => 'column1', - 'Type' => 'int(15)', - 'Null' => 'NO', - 'Default' => '', - 'Extra' => 'auto_increment' - ]; - - $column2 = [ - 'Field' => 'column2', - 'Type' => 'varchar(32)', - 'Null' => '', - 'Default' => 'NULL', - 'Extra' => '' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($column1)); - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($column2)); - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql('SHOW COLUMNS FROM `table_name`', $this->result); - - $this->assertFalse($this->adapter->hasColumn('table_name', 'column3')); - } - - public function testDropColumn() - { - $this->assertExecuteSql("ALTER TABLE `table_name` DROP COLUMN `column1`"); - $this->adapter->dropColumn('table_name', 'column1'); - } - - public function testAddColumn() - { - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName']) - ->getMock(); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - - $column = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit']) - ->getMock(); - - $column->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column->expects($this->any())->method('getAfter')->will($this->returnValue(null)); - $column->expects($this->any())->method('getLimit')->will($this->returnValue('11')); - $column->expects($this->any())->method('getType')->will($this->returnValue('integer')); - - $this->assertExecuteSql('ALTER TABLE `table_name` ADD `column_name` INT(11) NOT NULL'); - $this->adapter->addColumn($table, $column); - } - - public function testAddColumnWithAfter() - { - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName']) - ->getMock(); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - - $column = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit']) - ->getMock(); - - $column->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column->expects($this->any())->method('getAfter')->will($this->returnValue('column_name2')); - $column->expects($this->any())->method('getLimit')->will($this->returnValue('11')); - $column->expects($this->any())->method('getType')->will($this->returnValue('integer')); - - $this->assertExecuteSql('ALTER TABLE `table_name` ADD `column_name` INT(11) NOT NULL AFTER `column_name2`'); - $this->adapter->addColumn($table, $column); - } - - public function testChangeColumn() - { - $column = $this->getMockBuilder('Phinx\Db\Table\Column') - ->disableOriginalConstructor() - ->setMethods([ 'getName', 'getAfter', 'getType', 'getLimit']) - ->getMock(); - - $column->expects($this->any())->method('getName')->will($this->returnValue('column_name')); - $column->expects($this->any())->method('getLimit')->will($this->returnValue('11')); - $column->expects($this->any())->method('getType')->will($this->returnValue('integer')); - - $this->assertExecuteSql('ALTER TABLE `table_name` CHANGE `column1` `column_name` INT(11) NOT NULL'); - $this->adapter->changeColumn('table_name', 'column1', $column); - } - - public function testRenameColumnExists() - { - $column1 = [ - 'Field' => 'column_old', - 'Type' => 'int(15)', - 'Null' => 'NO', - 'Default' => '', - 'Extra' => 'auto_increment' - ]; - - $column2 = [ - 'Field' => 'column2', - 'Type' => 'varchar(32)', - 'Null' => '', - 'Default' => 'NULL', - 'Extra' => '' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($column1)); - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($column2)); - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql("DESCRIBE `table_name`", $this->result); - - $this->assertExecuteSql('ALTER TABLE `table_name` CHANGE COLUMN `column_old` `column_new` int(15) NOT NULL AUTO_INCREMENT'); - $this->adapter->renameColumn('table_name', 'column_old', 'column_new'); - } - - /** - * @expectedException InvalidArgumentException - * @expectedExceptionMessage The specified column doesn't exist: column_old - */ - public function testRenameColumnNotExists() - { - $column1 = [ - 'Field' => 'column1', - 'Type' => 'int(15)', - 'Null' => 'NO', - 'Default' => '', - 'Extra' => 'auto_increment' - ]; - - $column2 = [ - 'Field' => 'column2', - 'Type' => 'varchar(32)', - 'Null' => '', - 'Default' => 'NULL', - 'Extra' => '' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($column1)); - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($column2)); - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql("DESCRIBE `table_name`", $this->result); - - $this->adapter->renameColumn('table_name', 'column_old', 'column_new'); - } - - public function testGetDefaultValueDefinitionEmpty() - { - $this->assertEquals('', $this->adapter->getDefaultValueDefinition(null)); - $this->assertEquals('', $this->adapter->getDefaultValueDefinition('NULL')); - } - - public function testGetDefaultValueDefinitionBoolean() - { - $this->assertEquals( - ' DEFAULT 1', - $this->adapter->getDefaultValueDefinition(true) - ); - } - - public function testGetDefaultValueDefinitionInteger() - { - $this->assertEquals( - ' DEFAULT 5', - $this->adapter->getDefaultValueDefinition(5) - ); - } - - public function testGetDefaultValueDefinitionCurrentTimestamp() - { - $this->assertEquals( - ' DEFAULT CURRENT_TIMESTAMP', - $this->adapter->getDefaultValueDefinition('CURRENT_TIMESTAMP') - ); - } - - public function testGetDefaultValueDefinitionString() - { - $this->conn->expects($this->once()) - ->method('quote') - ->with($this->equalTo('str')) - ->will($this->returnValue("`str`")); - $this->assertEquals(' DEFAULT `str`', $this->adapter->getDefaultValueDefinition('str')); - } - - public function testGetSqlTypeExists() - { - $this->assertEquals( - ['name' => 'varchar', 'limit' => 255], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_STRING) - ); - $this->assertEquals( - ['name' => 'char', 'limit' => 255], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_CHAR, 255) - ); - - //text combinations - $this->assertEquals( - ['name' => 'text'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT) - ); - $this->assertEquals( - ['name' => 'tinytext'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_TINY) - ); - $this->assertEquals( - ['name' => 'tinytext'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_TINY + 1) - ); - $this->assertEquals( - ['name' => 'text'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_REGULAR) - ); - $this->assertEquals( - ['name' => 'text'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_REGULAR + 1) - ); - $this->assertEquals( - ['name' => 'mediumtext'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_MEDIUM) - ); - $this->assertEquals( - ['name' => 'mediumtext'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_MEDIUM + 1) - ); - $this->assertEquals( - ['name' => 'longtext'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_LONG) - ); - $this->assertEquals( - ['name' => 'longtext'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TEXT, MysqlAdapter::TEXT_LONG + 1) - ); - - //blob combinations - $this->assertEquals( - ['name' => 'blob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB) - ); - $this->assertEquals( - ['name' => 'tinyblob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_TINY) - ); - $this->assertEquals( - ['name' => 'tinyblob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_TINY + 1) - ); - $this->assertEquals( - ['name' => 'blob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_REGULAR) - ); - $this->assertEquals( - ['name' => 'blob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_REGULAR + 1) - ); - $this->assertEquals( - ['name' => 'mediumblob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_MEDIUM) - ); - $this->assertEquals( - ['name' => 'mediumblob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_MEDIUM + 1) - ); - $this->assertEquals( - ['name' => 'longblob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_LONG) - ); - $this->assertEquals( - ['name' => 'longblob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB, MysqlAdapter::BLOB_LONG + 1) - ); - - $this->assertEquals( - ['name' => 'binary', 'limit' => 255], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BINARY) - ); - $this->assertEquals( - ['name' => 'binary', 'limit' => 36], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BINARY, 36) - ); - - $this->assertEquals( - ['name' => 'varbinary', 'limit' => 255], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_VARBINARY) - ); - $this->assertEquals( - ['name' => 'varbinary', 'limit' => 16], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_VARBINARY, 16) - ); - - //int combinations - $this->assertEquals( - ['name' => 'int', 'limit' => 11], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER) - ); - $this->assertEquals( - ['name' => 'bigint', 'limit' => 20], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BIG_INTEGER) - ); - $this->assertEquals( - ['name' => 'tinyint'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_TINY) - ); - $this->assertEquals( - ['name' => 'tinyint'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_TINY + 1) - ); - $this->assertEquals( - ['name' => 'smallint'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_SMALL) - ); - $this->assertEquals( - ['name' => 'smallint'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_SMALL + 1) - ); - $this->assertEquals( - ['name' => 'mediumint'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_MEDIUM) - ); - $this->assertEquals( - ['name' => 'mediumint'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_MEDIUM + 1) - ); - $this->assertEquals( - ['name' => 'int', 'limit' => 11], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_REGULAR) - ); - $this->assertEquals( - ['name' => 'int', 'limit' => 11], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_REGULAR + 1) - ); - $this->assertEquals( - ['name' => 'bigint', 'limit' => 20], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_BIG) - ); - $this->assertEquals( - ['name' => 'bigint', 'limit' => 20], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_INTEGER, MysqlAdapter::INT_BIG + 1) - ); - - $this->assertEquals( - ['name' => 'float'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_FLOAT) - ); - $this->assertEquals( - ['name' => 'decimal'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_DECIMAL) - ); - $this->assertEquals( - ['name' => 'datetime', 'limit' => null], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_DATETIME) - ); - $this->assertEquals( - ['name' => 'timestamp', 'limit' => null], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TIMESTAMP) - ); - $this->assertEquals( - ['name' => 'date'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_DATE) - ); - $this->assertEquals( - ['name' => 'time', 'limit' => null], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_TIME) - ); - $this->assertEquals( - ['name' => 'blob'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BLOB) - ); - $this->assertEquals( - ['name' => 'tinyint', 'limit' => 1], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_BOOLEAN) - ); - $this->assertEquals( - ['name' => 'geometry'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_GEOMETRY) - ); - $this->assertEquals( - ['name' => 'linestring'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_LINESTRING) - ); - $this->assertEquals( - ['name' => 'point'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_POINT) - ); - $this->assertEquals( - ['name' => 'polygon'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_POLYGON) - ); - $this->assertEquals( - ['name' => 'enum'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_ENUM) - ); - $this->assertEquals( - ['name' => 'set'], - $this->adapter->getSqlType(MysqlAdapter::PHINX_TYPE_SET) - ); - } - - /** - * @expectedException RuntimeException - * @expectedExceptionMessage The type: "fake" is not supported. - */ - public function testGetSqlTypeNotExists() - { - $this->adapter->getSqlType('fake'); - } - - public function testPhinxTypeExistsWithoutLimit() - { - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_STRING, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('varchar') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_CHAR, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('char') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_INTEGER, 'limit' => MysqlAdapter::INT_TINY, 'precision' => null], - $this->adapter->getPhinxType('tinyint') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_INTEGER, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('int') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_INTEGER, 'limit' => MysqlAdapter::INT_SMALL, 'precision' => null], - $this->adapter->getPhinxType('smallint') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_INTEGER, 'limit' => MysqlAdapter::INT_MEDIUM, 'precision' => null], - $this->adapter->getPhinxType('mediumint') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BIG_INTEGER, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('bigint') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BINARY, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('blob') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_VARBINARY, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('varbinary') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_FLOAT, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('float') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_DECIMAL, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('decimal') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_DATETIME, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('datetime') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_TIMESTAMP, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('timestamp') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_DATE, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('date') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_TIME, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('time') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_TEXT, 'limit' => MysqlAdapter::TEXT_TINY, 'precision' => null], - $this->adapter->getPhinxType('tinytext') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_TEXT, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('text') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_TEXT, 'limit' => MysqlAdapter::TEXT_MEDIUM, 'precision' => null], - $this->adapter->getPhinxType('mediumtext') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_TEXT, 'limit' => MysqlAdapter::TEXT_LONG, 'precision' => null], - $this->adapter->getPhinxType('longtext') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BINARY, 'limit' => MysqlAdapter::BLOB_TINY, 'precision' => null], - $this->adapter->getPhinxType('tinyblob') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BINARY, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('blob') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BINARY, 'limit' => MysqlAdapter::BLOB_MEDIUM, 'precision' => null], - $this->adapter->getPhinxType('mediumblob') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BINARY, 'limit' => MysqlAdapter::BLOB_LONG, 'precision' => null], - $this->adapter->getPhinxType('longblob') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_POINT, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('point') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_GEOMETRY, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('geometry') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_LINESTRING, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('linestring') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_POLYGON, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('polygon') - ); - } - - public function testPhinxTypeExistsWithLimit() - { - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_STRING, 'limit' => 32, 'precision' => null], - $this->adapter->getPhinxType('varchar(32)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_CHAR, 'limit' => 32, 'precision' => null], - $this->adapter->getPhinxType('char(32)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_INTEGER, 'limit' => 12, 'precision' => null], - $this->adapter->getPhinxType('int(12)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BIG_INTEGER, 'limit' => 21, 'precision' => null], - $this->adapter->getPhinxType('bigint(21)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BINARY, 'limit' => 1024, 'precision' => null], - $this->adapter->getPhinxType('blob(1024)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_VARBINARY, 'limit' => 16, 'precision' => null], - $this->adapter->getPhinxType('varbinary(16)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_FLOAT, 'limit' => 8, 'precision' => 2], - $this->adapter->getPhinxType('float(8,2)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_DECIMAL, 'limit' => 8, 'precision' => 2], - $this->adapter->getPhinxType('decimal(8,2)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_TEXT, 'limit' => 1024, 'precision' => null], - $this->adapter->getPhinxType('text(1024)') - ); - } - - public function testPhinxTypeExistsWithLimitNull() - { - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_STRING, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('varchar(255)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_CHAR, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('char(255)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_INTEGER, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('int(11)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BIG_INTEGER, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('bigint(20)') - ); - $this->assertEquals( - ['name' => MysqlAdapter::PHINX_TYPE_BOOLEAN, 'limit' => null, 'precision' => null], - $this->adapter->getPhinxType('tinyint(1)') - ); - } - - /** - * @expectedException RuntimeException - * @expectedExceptionMessage The type: "fake" is not supported. - */ - public function testPhinxTypeNotValidType() - { - $this->adapter->getPhinxType('fake'); - } - - /** - * @expectedException RuntimeException - * @expectedExceptionMessage Column type ?int? is not supported - */ - public function testPhinxTypeNotValidTypeRegex() - { - $this->adapter->getPhinxType('?int?'); - } - - //index related tests - - public function testGetIndexSqlDefinitionRegular() - { - $index = $this->getMockBuilder('Phinx\Db\Table\Index') - ->disableOriginalConstructor() - ->setMethods([ 'getColumns', 'getName', 'getType']) - ->getMock(); - - $index->expects($this->any())->method('getColumns')->will($this->returnValue(['column_name'])); - $index->expects($this->any())->method('getName')->will($this->returnValue('index_name')); - $index->expects($this->any())->method('getType')->will($this->returnValue(\Phinx\Db\Table\Index::INDEX)); - $this->assertEquals(' KEY `index_name` (`column_name`)', $this->adapter->getIndexSqlDefinition($index)); - } - - public function testGetIndexSqlDefinitionUnique() - { - $index = $this->getMockBuilder('Phinx\Db\Table\Index') - ->disableOriginalConstructor() - ->setMethods([ 'getColumns', 'getName', 'getType']) - ->getMock(); - - $index->expects($this->any())->method('getColumns')->will($this->returnValue(['column_name'])); - $index->expects($this->any())->method('getName')->will($this->returnValue('index_name')); - $index->expects($this->any())->method('getType')->will($this->returnValue(\Phinx\Db\Table\Index::UNIQUE)); - $this->assertEquals(' UNIQUE KEY `index_name` (`column_name`)', $this->adapter->getIndexSqlDefinition($index)); - } - - public function testGetIndexesEmpty() - { - $this->result->expects($this->once()) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql("SHOW INDEXES FROM `table_name`", $this->result); - - $indexes = $this->adapter->getIndexes("table_name"); - - $this->assertEquals([], $indexes); - } - - private function prepareCaseIndexes() - { - $index1 = [ - 'Table' => 'table_name', - 'Non_unique' => '0', - 'Key_name' => 'PRIMARY', - 'Seq_in_index' => '1', - 'Column_name' => 'id', - 'Collation' => 'A', - 'Cardinality' => '0', - 'Sub_part' => 'NULL', - 'Packed' => 'NULL', - 'Null' => '', - 'Index_type' => 'BTREE', - 'Comment' => '', - 'Index_comment' => '' - ]; - - $index2 = [ - 'Table' => 'table_name', - 'Non_unique' => '0', - 'Key_name' => 'index_name', - 'Seq_in_index' => '1', - 'Column_name' => 'column_name', - 'Collation' => 'A', - 'Cardinality' => '0', - 'Sub_part' => 'NULL', - 'Packed' => 'NULL', - 'Null' => '', - 'Index_type' => 'BTREE', - 'Comment' => '', - 'Index_comment' => '' - ]; - - $index3 = [ - 'Table' => 'table_name', - 'Non_unique' => '0', - 'Key_name' => 'multiple_index_name', - 'Seq_in_index' => '1', - 'Column_name' => 'column_name', - 'Collation' => 'A', - 'Cardinality' => '0', - 'Sub_part' => 'NULL', - 'Packed' => 'NULL', - 'Null' => '', - 'Index_type' => 'BTREE', - 'Comment' => '', - 'Index_comment' => '' - ]; - - $index4 = [ - 'Table' => 'table_name', - 'Non_unique' => '0', - 'Key_name' => 'multiple_index_name', - 'Seq_in_index' => '2', - 'Column_name' => 'another_column_name', - 'Collation' => 'A', - 'Cardinality' => '0', - 'Sub_part' => 'NULL', - 'Packed' => 'NULL', - 'Null' => '', - 'Index_type' => 'BTREE', - 'Comment' => '', - 'Index_comment' => '' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($index1)); - - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($index2)); - - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue($index3)); - - $this->result->expects($this->at(3)) - ->method('fetch') - ->will($this->returnValue($index4)); - - $this->result->expects($this->at(4)) - ->method('fetch') - ->will($this->returnValue(null)); - - $this->assertQuerySql("SHOW INDEXES FROM `table_name`", $this->result); - - return [$index1, $index2, $index3, $index4]; - } - - public function testGetIndexes() - { - list($index1, $index2, $index3, $index4) = $this->prepareCaseIndexes(); - $indexes = $this->adapter->getIndexes("table_name"); - - $this->assertInternalType('array', $indexes); - $this->assertCount(3, $indexes); - $this->assertEquals(['columns' => [$index1['Column_name']]], $indexes[$index1['Key_name']]); - $this->assertEquals(['columns' => [$index2['Column_name']]], $indexes[$index2['Key_name']]); - $this->assertEquals(['columns' => [$index3['Column_name'], $index4['Column_name']]], $indexes[$index3['Key_name']]); - } - - public function testHasIndexExistsAsString() - { - $this->prepareCaseIndexes(); - $this->assertTrue($this->adapter->hasIndex("table_name", "column_name")); - } - - public function testHasIndexNotExistsAsString() - { - $this->prepareCaseIndexes(); - $this->assertFalse($this->adapter->hasIndex("table_name", "another_column_name")); - } - - public function testHasIndexExistsAsArray() - { - $this->prepareCaseIndexes(); - $this->assertTrue($this->adapter->hasIndex("table_name", ["column_name"])); - } - - public function testHasIndexNotExistsAsArray() - { - $this->prepareCaseIndexes(); - $this->assertFalse($this->adapter->hasIndex("table_name", ["another_column_name"])); - } - - public function testAddIndex() - { - list($table, $index) = $this->prepareAddIndex(['getColumns']); - - $this->assertExecuteSql('ALTER TABLE `table_name` ADD KEY (`column_name`)'); - $this->adapter->addIndex($table, $index); - } - - public function testAddIndexWithLimit() - { - list($table, $index) = $this->prepareAddIndex(['getColumns', 'getLimit']); - $index->expects($this->any())->method('getLimit')->will($this->returnValue(50)); - - $this->assertExecuteSql('ALTER TABLE `table_name` ADD KEY (`column_name`(50))'); - $this->adapter->addIndex($table, $index); - } - - /** - * @param array $methods - * @return array - */ - private function prepareAddIndex($methods) - { - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName']) - ->getMock(); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - - $index = $this->getMockBuilder('Phinx\Db\Table\Index') - ->disableOriginalConstructor() - ->setMethods($methods) - ->getMock(); - - $index->expects($this->any())->method('getColumns')->will($this->returnValue(['column_name'])); - - return [$table, $index]; - } - - public function testDropIndexAsString() - { - $this->prepareCaseIndexes(); - $this->assertExecuteSql('ALTER TABLE `table_name` DROP INDEX `index_name`'); - $this->adapter->dropIndex('table_name', 'column_name'); - } - - public function testDropIndexAsArray() - { - $this->prepareCaseIndexes(); - $this->assertExecuteSql('ALTER TABLE `table_name` DROP INDEX `index_name`'); - $this->adapter->dropIndex('table_name', ['column_name']); - } - - public function testDropIndexByName() - { - $this->prepareCaseIndexes(); - $this->assertExecuteSql('ALTER TABLE `table_name` DROP INDEX `index_name`'); - $this->adapter->dropIndexByName('table_name', 'index_name'); - } - - //foregnkey related tests - - private function prepareCaseForeignKeys() - { - $fk = [ - 'CONSTRAINT_NAME' => 'fk1', - 'TABLE_NAME' => 'table_name', - 'COLUMN_NAME' => 'other_table_id', - 'REFERENCED_TABLE_NAME' => 'other_table', - 'REFERENCED_COLUMN_NAME' => 'id' - ]; - - $fk1 = [ - 'CONSTRAINT_NAME' => 'fk2', - 'TABLE_NAME' => 'table_name', - 'COLUMN_NAME' => 'other_table_id', - 'REFERENCED_TABLE_NAME' => 'other_table', - 'REFERENCED_COLUMN_NAME' => 'id' - ]; - - $fk2 = [ - 'CONSTRAINT_NAME' => 'fk2', - 'TABLE_NAME' => 'table_name', - 'COLUMN_NAME' => 'another_table_id', - 'REFERENCED_TABLE_NAME' => 'other_table', - 'REFERENCED_COLUMN_NAME' => 'id' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($fk)); - - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue($fk1)); - - $this->result->expects($this->at(2)) - ->method('fetch') - ->will($this->returnValue($fk2)); - - $this->result->expects($this->at(3)) - ->method('fetch') - ->will($this->returnValue(null)); - - $expectedSql = 'SELECT - CONSTRAINT_NAME, - TABLE_NAME, - COLUMN_NAME, - REFERENCED_TABLE_NAME, - REFERENCED_COLUMN_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE REFERENCED_TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - AND TABLE_NAME = \'table_name\' - ORDER BY POSITION_IN_UNIQUE_CONSTRAINT'; - $this->assertQuerySql($expectedSql, $this->result); - - return [$fk, $fk1, $fk2]; - } - - public function testGetForeignKeys() - { - list($fk, $fk1, $fk2) = $this->prepareCaseForeignKeys(); - $foreignkeys = $this->adapter->getForeignKeys("table_name"); - - $this->assertInternalType('array', $foreignkeys); - $this->assertCount(2, $foreignkeys); - $this->assertEquals('table_name', $foreignkeys['fk1']['table']); - $this->assertEquals(['other_table_id'], $foreignkeys['fk1']['columns']); - $this->assertEquals('other_table', $foreignkeys['fk1']['referenced_table']); - $this->assertEquals(['id'], $foreignkeys['fk1']['referenced_columns']); - } - - public function testHasForeignKeyExistsAsString() - { - $this->prepareCaseForeignKeys(); - $this->assertTrue($this->adapter->hasForeignKey("table_name", "other_table_id")); - } - - public function testHasForeignKeyExistsAsStringAndConstraint() - { - $this->prepareCaseForeignKeys(); - $this->assertTrue($this->adapter->hasForeignKey("table_name", "other_table_id", 'fk1')); - } - - public function testHasForeignKeyNotExistsAsString() - { - $this->prepareCaseForeignKeys(); - $this->assertFalse($this->adapter->hasForeignKey("table_name", "another_table_id")); - } - - public function testHasForeignKeyNotExistsAsStringAndConstraint() - { - $this->prepareCaseForeignKeys(); - $this->assertFalse($this->adapter->hasForeignKey("table_name", "other_table_id", 'fk3')); - } - - public function testHasForeignKeyExistsAsArray() - { - $this->prepareCaseForeignKeys(); - $this->assertTrue($this->adapter->hasForeignKey("table_name", ["other_table_id"])); - } - - public function testHasForeignKeyExistsAsArrayAndConstraint() - { - $this->prepareCaseForeignKeys(); - $this->assertTrue($this->adapter->hasForeignKey("table_name", ["other_table_id"], 'fk1')); - } - - public function testHasForeignKeyNotExistsAsArray() - { - $this->prepareCaseForeignKeys(); - $this->assertFalse($this->adapter->hasForeignKey("table_name", ["another_table_id"])); - } - - public function testHasForeignKeyNotExistsAsArrayAndConstraint() - { - $this->prepareCaseForeignKeys(); - $this->assertFalse($this->adapter->hasForeignKey("table_name", ["other_table_id"], 'fk3')); - } - - public function testAddForeignKeyBasic() - { - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName']) - ->getMock(); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - - $refTable = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName']) - ->getMock(); - $refTable->expects($this->any())->method('getName')->will($this->returnValue('other_table')); - - $foreignkey = $this->getMockBuilder('Phinx\Db\Table\ForeignKey') - ->disableOriginalConstructor() - ->setMethods([ 'getColumns', - 'getConstraint', - 'getReferencedColumns', - 'getOnDelete', - 'getOnUpdate', - 'getReferencedTable']) - ->getMock(); - - $foreignkey->expects($this->any())->method('getColumns')->will($this->returnValue(['other_table_id'])); - $foreignkey->expects($this->any())->method('getConstraint')->will($this->returnValue('fk1')); - $foreignkey->expects($this->any())->method('getReferencedColumns')->will($this->returnValue(['id'])); - $foreignkey->expects($this->any())->method('getReferencedTable')->will($this->returnValue($refTable)); - $foreignkey->expects($this->any())->method('getOnDelete')->will($this->returnValue(null)); - $foreignkey->expects($this->any())->method('getOnUpdate')->will($this->returnValue(null)); - - $this->assertExecuteSql('ALTER TABLE `table_name` ADD CONSTRAINT `fk1` FOREIGN KEY (`other_table_id`) REFERENCES `other_table` (`id`)'); - $this->adapter->addForeignKey($table, $foreignkey); - } - - public function testAddForeignKeyComplete() - { - $table = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName']) - ->getMock(); - $table->expects($this->any())->method('getName')->will($this->returnValue('table_name')); - - $refTable = $this->getMockBuilder('Phinx\Db\Table') - ->disableOriginalConstructor() - ->setMethods(['getName']) - ->getMock(); - $refTable->expects($this->any())->method('getName')->will($this->returnValue('other_table')); - - $foreignkey = $this->getMockBuilder('Phinx\Db\Table\ForeignKey') - ->disableOriginalConstructor() - ->setMethods([ 'getColumns', - 'getConstraint', - 'getReferencedColumns', - 'getOnDelete', - 'getOnUpdate', - 'getReferencedTable']) - ->getMock(); - - $foreignkey->expects($this->any())->method('getColumns')->will($this->returnValue(['other_table_id'])); - $foreignkey->expects($this->any())->method('getConstraint')->will($this->returnValue('fk1')); - $foreignkey->expects($this->any())->method('getReferencedColumns')->will($this->returnValue(['id'])); - $foreignkey->expects($this->any())->method('getReferencedTable')->will($this->returnValue($refTable)); - $foreignkey->expects($this->any())->method('getOnDelete')->will($this->returnValue('CASCADE')); - $foreignkey->expects($this->any())->method('getOnUpdate')->will($this->returnValue('CASCADE')); - - $this->assertExecuteSql('ALTER TABLE `table_name` ADD CONSTRAINT `fk1` FOREIGN KEY (`other_table_id`) REFERENCES `other_table` (`id`) ON DELETE CASCADE ON UPDATE CASCADE'); - $this->adapter->addForeignKey($table, $foreignkey); - } - - public function testDropForeignKeyAsString() - { - $fk = [ - 'CONSTRAINT_NAME' => 'fk1', - 'TABLE_NAME' => 'table_name', - 'COLUMN_NAME' => 'other_table_id', - 'REFERENCED_TABLE_NAME' => 'other_table', - 'REFERENCED_COLUMN_NAME' => 'id' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($fk)); - - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue(null)); - - $expectedSql = 'SELECT - CONSTRAINT_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE REFERENCED_TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - AND TABLE_NAME = \'table_name\' - AND COLUMN_NAME = \'column_name\' - ORDER BY POSITION_IN_UNIQUE_CONSTRAINT'; - $this->assertQuerySql($expectedSql, $this->result); - - $this->assertExecuteSql('ALTER TABLE `table_name` DROP FOREIGN KEY fk1'); - $this->adapter->dropForeignKey('table_name', 'column_name'); - } - - public function _testDropForeignKeyAsArray() - { - $fk = [ - 'CONSTRAINT_NAME' => 'fk1', - 'TABLE_NAME' => 'table_name', - 'COLUMN_NAME' => 'other_table_id', - 'REFERENCED_TABLE_NAME' => 'other_table', - 'REFERENCED_COLUMN_NAME' => 'id' - ]; - - $this->result->expects($this->at(0)) - ->method('fetch') - ->will($this->returnValue($fk)); - - $this->result->expects($this->at(1)) - ->method('fetch') - ->will($this->returnValue(null)); - - $expectedSql = 'SELECT - CONSTRAINT_NAME - FROM information_schema.KEY_COLUMN_USAGE - WHERE REFERENCED_TABLE_SCHEMA = DATABASE() - AND REFERENCED_TABLE_NAME IS NOT NULL - AND TABLE_NAME = \'table_name\' - AND COLUMN_NAME = \'column_name\' - ORDER BY POSITION_IN_UNIQUE_CONSTRAINT'; - $this->assertQuerySql($expectedSql, $this->result); - - $this->assertExecuteSql('ALTER TABLE `table_name` DROP FOREIGN KEY fk1'); - $this->adapter->dropForeignKey('table_name', ['column_name']); - } - - public function testDropForeignKeyAsStringByConstraint() - { - $this->assertExecuteSql('ALTER TABLE `table_name` DROP FOREIGN KEY fk1'); - $this->adapter->dropForeignKey('table_name', 'column_name', 'fk1'); - } - - public function _testDropForeignKeyAsArrayByConstraint() - { - $this->assertExecuteSql('ALTER TABLE `table_name` DROP FOREIGN KEY fk1'); - $this->adapter->dropForeignKey('table_name', ['column_name'], 'fk1'); - } -} diff --git a/tests/Phinx/Db/Adapter/PostgresAdapterTest.php b/tests/Phinx/Db/Adapter/PostgresAdapterTest.php index 56c24dd6f..148d14ffc 100644 --- a/tests/Phinx/Db/Adapter/PostgresAdapterTest.php +++ b/tests/Phinx/Db/Adapter/PostgresAdapterTest.php @@ -241,7 +241,8 @@ public function testRenameTable() $table->save(); $this->assertTrue($this->adapter->hasTable('table1')); $this->assertFalse($this->adapter->hasTable('table2')); - $this->adapter->renameTable('table1', 'table2'); + + $table->rename('table2')->save(); $this->assertFalse($this->adapter->hasTable('table1')); $this->assertTrue($this->adapter->hasTable('table2')); } @@ -530,23 +531,15 @@ public function testChangeColumn() $table->addColumn('column1', 'string') ->save(); $this->assertTrue($this->adapter->hasColumn('t', 'column1')); - $newColumn1 = new \Phinx\Db\Table\Column(); - $newColumn1->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', 'string')->save(); $this->assertTrue($this->adapter->hasColumn('t', 'column1')); + $newColumn2 = new \Phinx\Db\Table\Column(); $newColumn2->setName('column2') - ->setType('string') - ->setNull(true); - $table->changeColumn('column1', $newColumn2); + ->setType('string'); + $table->changeColumn('column1', $newColumn2)->save(); $this->assertFalse($this->adapter->hasColumn('t', 'column1')); $this->assertTrue($this->adapter->hasColumn('t', 'column2')); - $columns = $this->adapter->getColumns('t'); - foreach ($columns as $column) { - if ($column->getName() == 'column2') { - $this->assertTrue($column->isNull()); - } - } } public function testChangeColumnWithDefault() @@ -561,7 +554,7 @@ public function testChangeColumnWithDefault() ->setNull(true); $newColumn1->setDefault('Test'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $columns = $this->adapter->getColumns('t'); foreach ($columns as $column) { @@ -589,7 +582,7 @@ public function testChangeColumnWithDropDefault() $newColumn1->setName('column1') ->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $columns = $this->adapter->getColumns('t'); foreach ($columns as $column) { @@ -605,34 +598,53 @@ public function testDropColumn() $table->addColumn('column1', 'string') ->save(); $this->assertTrue($this->adapter->hasColumn('t', 'column1')); - $this->adapter->dropColumn('t', 'column1'); + + $table->removeColumn('column1')->save(); $this->assertFalse($this->adapter->hasColumn('t', 'column1')); } - public function testGetColumns() + public function columnsProvider() + { + return [ + ['column1', 'string', []], + ['column2', 'integer', ['limit' => PostgresAdapter::INT_SMALL], 'smallint'], + ['column2_1', 'integer', []], + ['column3', 'biginteger', []], + ['column4', 'text', []], + ['column5', 'float', []], + ['column6', 'decimal', []], + ['column7', 'datetime', []], + ['column8', 'time', []], + ['column9', 'timestamp', [], 'datetime'], + ['column10', 'date', []], + ['column11', 'binary', []], + ['column12', 'boolean', []], + ['column13', 'string', ['limit' => 10]], + ['column16', 'interval', []], + ]; + } + + /** + * + * @dataProvider columnsProvider + */ + public function testGetColumns($colName, $type, $options, $actualType = null) { $table = new \Phinx\Db\Table('t', [], $this->adapter); - $table->addColumn('column1', 'string') - ->addColumn('column2', 'integer', ['limit' => PostgresAdapter::INT_SMALL]) - ->addColumn('column3', 'integer') - ->addColumn('column4', 'biginteger') - ->addColumn('column5', 'text') - ->addColumn('column6', 'float') - ->addColumn('column7', 'decimal') - ->addColumn('column8', 'time') - ->addColumn('column9', 'timestamp') - ->addColumn('column10', 'date') - ->addColumn('column11', 'boolean') - ->addColumn('column12', 'datetime') - ->addColumn('column13', 'binary') - ->addColumn('column14', 'string', ['limit' => 10]) - ->addColumn('column15', 'interval'); - $pendingColumns = $table->getPendingColumns(); - $table->save(); + $table->addColumn($colName, $type, $options)->save(); + $columns = $this->adapter->getColumns('t'); - $this->assertCount(count($pendingColumns) + 1, $columns); - for ($i = 0; $i++; $i < count($pendingColumns)) { - $this->assertEquals($pendingColumns[$i], $columns[$i + 1]); + $this->assertCount(2, $columns); + $this->assertEquals($colName, $columns[1]->getName()); + + if (!$actualType) { + $actualType = $type; + } + + if (is_string($columns[1]->getType())) { + $this->assertEquals($actualType, $columns[1]->getType()); + } else { + $this->assertEquals(['name' => $actualType] + $options, $columns[1]->getType()); } } @@ -730,16 +742,12 @@ public function testAddForeignKey() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']) - ->setConstraint('fk1'); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); - $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'], 'fk1')); + $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } public function testDropForeignKey() @@ -748,16 +756,12 @@ public function testDropForeignKey() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); - $this->adapter->addForeignKey($table, $fk); - $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); - $this->adapter->dropForeignKey($table->getName(), ['ref_table_id']); + $table->dropForeignKey(['ref_table_id'])->save(); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } @@ -1079,28 +1083,36 @@ public function testTimestampWithTimezone() public function testBulkInsertData() { + $data = [ + [ + 'column1' => 'value1', + 'column2' => 1, + ], + [ + 'column1' => 'value2', + 'column2' => 2, + ], + [ + 'column1' => 'value3', + 'column2' => 3, + ] + ]; $table = new \Phinx\Db\Table('table1', [], $this->adapter); $table->addColumn('column1', 'string') - ->addColumn('column2', 'integer') - ->insert([ - [ - 'column1' => 'value1', - 'column2' => 1 - ], - [ - 'column1' => 'value2', - 'column2' => 2 - ] - ]); - $this->adapter->createTable($table); - $this->adapter->bulkinsert($table, $table->getData()); - $table->reset(); + ->addColumn('column2', 'integer') + ->addColumn('column3', 'string', ['default' => 'test']) + ->insert($data) + ->save(); $rows = $this->adapter->fetchAll('SELECT * FROM table1'); $this->assertEquals('value1', $rows[0]['column1']); $this->assertEquals('value2', $rows[1]['column1']); + $this->assertEquals('value3', $rows[2]['column1']); $this->assertEquals(1, $rows[0]['column2']); $this->assertEquals(2, $rows[1]['column2']); + $this->assertEquals(3, $rows[2]['column2']); + $this->assertEquals('test', $rows[0]['column3']); + $this->assertEquals('test', $rows[2]['column3']); } public function testInsertData() @@ -1191,15 +1203,15 @@ public function testDumpInsert() $consoleOutput = new BufferedOutput(); $this->adapter->setOutput($consoleOutput); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'string_col' => 'test data' ]); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'string_col' => null ]); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'int_col' => 23 ]); @@ -1235,7 +1247,7 @@ public function testDumpBulkinsert() $consoleOutput = new BufferedOutput(); $this->adapter->setOutput($consoleOutput); - $this->adapter->bulkinsert($table, [ + $this->adapter->bulkinsert($table->getTable(), [ [ 'string_col' => 'test_data1', 'int_col' => 23, diff --git a/tests/Phinx/Db/Adapter/ProxyAdapterTest.php b/tests/Phinx/Db/Adapter/ProxyAdapterTest.php index 02f6b678d..3c268a47d 100644 --- a/tests/Phinx/Db/Adapter/ProxyAdapterTest.php +++ b/tests/Phinx/Db/Adapter/ProxyAdapterTest.php @@ -21,8 +21,13 @@ public function setUp() { $stub = $this->getMockBuilder('\Phinx\Db\Adapter\PdoAdapter') ->setConstructorArgs([[]]) + ->setMethods([]) ->getMock(); + $stub->expects($this->any()) + ->method('isValidColumnType') + ->will($this->returnValue(true)); + $this->adapter = new ProxyAdapter($stub); } @@ -33,88 +38,113 @@ public function tearDown() public function testProxyAdapterCanInvertCreateTable() { - $table = new \Phinx\Db\Table('atable'); - $this->adapter->createTable($table); + $table = new \Phinx\Db\Table('atable', [], $this->adapter); + $table->addColumn('column1', 'string') + ->save(); - $commands = $this->adapter->getInvertedCommands(); - $this->assertEquals('dropTable', $commands[0]['name']); - $this->assertEquals('atable', $commands[0]['arguments'][0]); + $commands = $this->adapter->getInvertedCommands()->getActions(); + $this->assertInstanceOf('Phinx\Db\Action\DropTable', $commands[0]); + $this->assertEquals('atable', $commands[0]->getTable()->getName()); } public function testProxyAdapterCanInvertRenameTable() { - $this->adapter->renameTable('oldname', 'newname'); - - $commands = $this->adapter->getInvertedCommands(); - $this->assertEquals('renameTable', $commands[0]['name']); - $this->assertEquals('newname', $commands[0]['arguments'][0]); - $this->assertEquals('oldname', $commands[0]['arguments'][1]); + $table = new \Phinx\Db\Table('oldname', [], $this->adapter); + $table->rename('newname') + ->save(); + + $commands = $this->adapter->getInvertedCommands()->getActions(); + $this->assertInstanceOf('Phinx\Db\Action\RenameTable', $commands[0]); + $this->assertEquals('newname', $commands[0]->getTable()->getName()); + $this->assertEquals('oldname', $commands[0]->getNewName()); } public function testProxyAdapterCanInvertAddColumn() { - $table = new \Phinx\Db\Table('atable'); - $column = new \Phinx\Db\Table\Column(); - $column->setName('acolumn'); - - $this->adapter->addColumn($table, $column); - - $commands = $this->adapter->getInvertedCommands(); - $this->assertEquals('dropColumn', $commands[0]['name']); - $this->assertEquals('atable', $commands[0]['arguments'][0]); - $this->assertContains('acolumn', $commands[0]['arguments'][1]); + $this->adapter + ->getAdapter() + ->expects($this->any()) + ->method('hasTable') + ->will($this->returnValue(true)); + $table = new \Phinx\Db\Table('atable', [], $this->adapter); + $table->addColumn('acolumn', 'string') + ->save(); + + $commands = $this->adapter->getInvertedCommands()->getActions(); + $this->assertInstanceOf('Phinx\Db\Action\RemoveColumn', $commands[0]); + $this->assertEquals('atable', $commands[0]->getTable()->getName()); + $this->assertEquals('acolumn', $commands[0]->getColumn()->getName()); } public function testProxyAdapterCanInvertRenameColumn() { - $this->adapter->renameColumn('atable', 'oldname', 'newname'); - - $commands = $this->adapter->getInvertedCommands(); - $this->assertEquals('renameColumn', $commands[0]['name']); - $this->assertEquals('atable', $commands[0]['arguments'][0]); - $this->assertEquals('newname', $commands[0]['arguments'][1]); - $this->assertEquals('oldname', $commands[0]['arguments'][2]); + $this->adapter + ->getAdapter() + ->expects($this->any()) + ->method('hasTable') + ->will($this->returnValue(true)); + + $table = new \Phinx\Db\Table('atable', [], $this->adapter); + $table->renameColumn('oldname', 'newname') + ->save(); + + $commands = $this->adapter->getInvertedCommands()->getActions(); + $this->assertInstanceOf('Phinx\Db\Action\RenameColumn', $commands[0]); + $this->assertEquals('newname', $commands[0]->getColumn()->getName()); + $this->assertEquals('oldname', $commands[0]->getNewName()); } public function testProxyAdapterCanInvertAddIndex() { - $table = new \Phinx\Db\Table('atable'); - $index = new \Phinx\Db\Table\Index(); - $index->setType(\Phinx\Db\Table\Index::INDEX) - ->setColumns(['email']); - - $this->adapter->addIndex($table, $index); - - $commands = $this->adapter->getInvertedCommands(); - $this->assertEquals('dropIndex', $commands[0]['name']); - $this->assertEquals('atable', $commands[0]['arguments'][0]); - $this->assertContains('email', $commands[0]['arguments'][1]); + $this->adapter + ->getAdapter() + ->expects($this->any()) + ->method('hasTable') + ->will($this->returnValue(true)); + + $table = new \Phinx\Db\Table('atable', [], $this->adapter); + $table->addIndex(['email']) + ->save(); + + $commands = $this->adapter->getInvertedCommands()->getActions(); + $this->assertInstanceOf('Phinx\Db\Action\DropIndex', $commands[0]); + $this->assertEquals('atable', $commands[0]->getTable()->getName()); + $this->assertEquals(['email'], $commands[0]->getIndex()->getColumns()); } public function testProxyAdapterCanInvertAddForeignKey() { - $table = new \Phinx\Db\Table('atable'); - $refTable = new \Phinx\Db\Table('refTable'); - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); - - $this->adapter->addForeignKey($table, $fk); - - $commands = $this->adapter->getInvertedCommands(); - $this->assertEquals('dropForeignKey', $commands[0]['name']); - $this->assertEquals('atable', $commands[0]['arguments'][0]); - $this->assertContains('ref_table_id', $commands[0]['arguments'][1]); + $this->adapter + ->getAdapter() + ->expects($this->any()) + ->method('hasTable') + ->will($this->returnValue(true)); + + $table = new \Phinx\Db\Table('atable', [], $this->adapter); + $table->addForeignKey(['ref_table_id'], 'refTable') + ->save(); + + $commands = $this->adapter->getInvertedCommands()->getActions(); + $this->assertInstanceOf('Phinx\Db\Action\DropForeignKey', $commands[0]); + $this->assertEquals('atable', $commands[0]->getTable()->getName()); + $this->assertEquals(['ref_table_id'], $commands[0]->getForeignKey()->getColumns()); } /** * @expectedException \Phinx\Migration\IrreversibleMigrationException - * @expectedExceptionMessage Cannot reverse a "createDatabase" command + * @expectedExceptionMessage Cannot reverse a "Phinx\Db\Action\RemoveColumn" command */ public function testGetInvertedCommandsThrowsExceptionForIrreversibleCommand() { - $this->adapter->recordCommand('createDatabase', ['testdb']); + $this->adapter + ->getAdapter() + ->expects($this->any()) + ->method('hasTable') + ->will($this->returnValue(true)); + + $table = new \Phinx\Db\Table('atable', [], $this->adapter); + $table->removeColumn('thing') + ->save(); $this->adapter->getInvertedCommands(); } } diff --git a/tests/Phinx/Db/Adapter/SQLiteAdapterTest.php b/tests/Phinx/Db/Adapter/SQLiteAdapterTest.php index dd7a75754..bda76516d 100644 --- a/tests/Phinx/Db/Adapter/SQLiteAdapterTest.php +++ b/tests/Phinx/Db/Adapter/SQLiteAdapterTest.php @@ -48,8 +48,6 @@ public function testConnection() public function testBeginTransaction() { - $this->adapter->getConnection() - ->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); $this->adapter->beginTransaction(); $this->assertTrue( @@ -160,12 +158,6 @@ public function testCreateTableIdentityIdColumn() $this->assertEquals(true, $idColumn->getIdentity()); } - public function testCreateTableWithNoOptions() - { - $this->markTestIncomplete(); - //$this->adapter->createTable('ntable', ) - } - public function testCreateTableWithNoPrimaryKey() { $options = [ @@ -346,7 +338,7 @@ public function testChangeColumn() $newColumn2 = new \Phinx\Db\Table\Column(); $newColumn2->setName('column2') ->setType('string'); - $table->changeColumn('column1', $newColumn2); + $table->changeColumn('column1', $newColumn2)->save(); $this->assertFalse($this->adapter->hasColumn('t', 'column1')); $this->assertTrue($this->adapter->hasColumn('t', 'column2')); } @@ -359,7 +351,7 @@ public function testChangeColumnDefaultValue() $newColumn1 = new \Phinx\Db\Table\Column(); $newColumn1->setDefault('test1') ->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $rows = $this->adapter->fetchAll('pragma table_info(t)'); $this->assertEquals("'test1'", $rows[1]['dflt_value']); @@ -374,18 +366,14 @@ public function testChangeColumnWithForeignKey() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('another_table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); - - $this->adapter->addForeignKey($table, $fk); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); - $table->changeColumn('ref_table_id', 'float'); + $table->changeColumn('ref_table_id', 'float')->save(); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } @@ -398,7 +386,7 @@ public function testChangeColumnDefaultToZero() $newColumn1 = new \Phinx\Db\Table\Column(); $newColumn1->setDefault(0) ->setType('integer'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $rows = $this->adapter->fetchAll('pragma table_info(t)'); $this->assertEquals("0", $rows[1]['dflt_value']); } @@ -411,7 +399,7 @@ public function testChangeColumnDefaultToNull() $newColumn1 = new \Phinx\Db\Table\Column(); $newColumn1->setDefault(null) ->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $rows = $this->adapter->fetchAll('pragma table_info(t)'); $this->assertNull($rows[1]['dflt_value']); } @@ -425,7 +413,7 @@ public function testChangeColumnWithCommasInCommentsOrDefaultValue() $newColumn1->setDefault('another default') ->setComment('another comment') ->setType('string'); - $table->changeColumn('column1', $newColumn1); + $table->changeColumn('column1', $newColumn1)->save(); $rows = $this->adapter->fetchAll('pragma table_info(t)'); $this->assertEquals("'another default'", $rows[1]['dflt_value']); } @@ -441,7 +429,7 @@ public function testDropColumn($columnCreationArgs) $table->save(); $this->assertTrue($this->adapter->hasColumn('t', $columnName)); - $this->adapter->dropColumn('t', $columnName); + $table->removeColumn($columnName)->save(); $this->assertFalse($this->adapter->hasColumn('t', $columnName)); } @@ -454,35 +442,49 @@ public function columnCreationArgumentProvider() ]; } - public function testGetColumns() + public function columnsProvider() + { + return [ + ['column1', 'string', []], + ['column2', 'integer', []], + ['column3', 'biginteger', []], + ['column4', 'text', []], + ['column5', 'float', []], + ['column6', 'decimal', []], + ['column7', 'datetime', []], + ['column8', 'time', []], + ['column9', 'timestamp', [], 'datetime'], + ['column10', 'date', []], + ['column11', 'binary', []], + ['column13', 'string', ['limit' => 10]], + ['column15', 'integer', ['limit' => 10]], + ['column22', 'enum', ['values' => ['three', 'four']]], + ['column23', 'json', [], 'text'], + ]; + } + + /** + * + * @dataProvider columnsProvider + */ + public function testGetColumns($colName, $type, $options, $actualType = null) { $table = new \Phinx\Db\Table('t', [], $this->adapter); - $table->addColumn('column1', 'string') - ->addColumn('column2', 'integer') - ->addColumn('column3', 'biginteger') - ->addColumn('column4', 'text') - ->addColumn('column5', 'float') - ->addColumn('column6', 'decimal') - ->addColumn('column7', 'datetime') - ->addColumn('column8', 'time') - ->addColumn('column9', 'timestamp') - ->addColumn('column10', 'date') - ->addColumn('column11', 'binary') - ->addColumn('column12', 'boolean') - ->addColumn('column13', 'string', ['limit' => 10]) - ->addColumn('column15', 'integer', ['limit' => 10]) - ->addColumn('column16', 'enum', ['values' => ['a', 'b', 'c']]) - ->addColumn('column17', 'json'); - $pendingColumns = $table->getPendingColumns(); - $table->save(); + $table->addColumn($colName, $type, $options)->save(); + $columns = $this->adapter->getColumns('t'); - $this->assertCount(count($pendingColumns) + 1, $columns); - for ($i = 0; $i++; $i < count($pendingColumns)) { - $this->assertEquals($pendingColumns[$i], $columns[$i + 1]); + $this->assertCount(2, $columns); + $this->assertEquals($colName, $columns[1]->getName()); + + if (!$actualType) { + $actualType = $type; + } + + if (is_string($columns[1]->getType())) { + $this->assertEquals($actualType, $columns[1]->getType()); + } else { + $this->assertEquals(['name' => $actualType] + $options, $columns[1]->getType()); } - // check the json column is actually of type TEXT - $rows = $this->adapter->fetchAll('pragma table_info(t)'); - $this->assertEquals('TEXT', $rows[16]['type']); } public function testAddIndex() @@ -565,34 +567,10 @@ public function testAddForeignKey() $refTable->addColumn('field1', 'string')->save(); $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); - - $this->adapter->addForeignKey($table, $fk); - - $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); - } - - public function testAddForeignKeyWithPdoExceptionErrorMode() - { - $this->adapter->getConnection() - ->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - $refTable = new \Phinx\Db\Table('ref_table', [], $this->adapter); - $refTable->addColumn('field1', 'string')->save(); - - $table = new \Phinx\Db\Table('table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); - - $this->adapter->addForeignKey($table, $fk); + $table + ->addColumn('ref_table_id', 'integer') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->save(); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); } @@ -605,31 +583,22 @@ public function testDropForeignKey() ->save(); $table = new \Phinx\Db\Table('another_table', [], $this->adapter); - $table->addColumn('ref_table_id', 'integer')->addColumn('ref_table_field', 'string')->save(); - - $fk = new \Phinx\Db\Table\ForeignKey(); - $fk->setReferencedTable($refTable) - ->setColumns(['ref_table_id']) - ->setReferencedColumns(['id']); - - $secondFk = new \Phinx\Db\Table\ForeignKey(); - $secondFk->setReferencedTable($refTable) - ->setColumns(['ref_table_field']) - ->setReferencedColumns(['field1']) - ->setOptions([ - 'update' => 'CASCADE', - 'delete' => 'CASCADE' - ]); - - $this->adapter->addForeignKey($table, $fk); + $opts = [ + 'update' => 'CASCADE', + 'delete' => 'CASCADE' + ]; + $table + ->addColumn('ref_table_id', 'integer') + ->addColumn('ref_table_field', 'string') + ->addForeignKey(['ref_table_id'], 'ref_table', ['id']) + ->addForeignKey(['ref_table_field'], 'ref_table', ['field1'], $opts) + ->save(); + $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); $this->adapter->dropForeignKey($table->getName(), ['ref_table_id']); $this->assertFalse($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); - $this->adapter->addForeignKey($table, $secondFk); - $this->adapter->addForeignKey($table, $fk); - $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_id'])); $this->assertTrue($this->adapter->hasForeignKey($table->getName(), ['ref_table_field'])); $this->adapter->dropForeignKey($table->getName(), ['ref_table_field']); @@ -732,11 +701,8 @@ public function testBulkInsertData() 'column1' => '\'value4\'', 'column2' => null, ] - ); - $this->adapter->createTable($table); - $this->adapter->bulkinsert($table, $table->getData()); - $table->reset(); - + ) + ->save(); $rows = $this->adapter->fetchAll('SELECT * FROM table1'); $this->assertEquals('value1', $rows[0]['column1']); @@ -798,10 +764,8 @@ public function testBulkInsertDataEnum() ->addColumn('column3', 'enum', ['values' => ['a', 'b', 'c'], 'default' => 'c']) ->insert([ 'column1' => 'a', - ]); - $this->adapter->createTable($table); - $this->adapter->bulkinsert($table, $table->getData()); - $table->reset(); + ]) + ->save(); $rows = $this->adapter->fetchAll('SELECT * FROM table1'); @@ -931,15 +895,15 @@ public function testDumpInsert() $consoleOutput = new BufferedOutput(); $this->adapter->setOutput($consoleOutput); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'string_col' => 'test data' ]); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'string_col' => null ]); - $this->adapter->insert($table, [ + $this->adapter->insert($table->getTable(), [ 'int_col' => 23 ]); @@ -975,7 +939,7 @@ public function testDumpBulkinsert() $consoleOutput = new BufferedOutput(); $this->adapter->setOutput($consoleOutput); - $this->adapter->bulkinsert($table, [ + $this->adapter->bulkinsert($table->getTable(), [ [ 'string_col' => 'test_data1', 'int_col' => 23, diff --git a/tests/Phinx/Db/Adapter/TablePrefixAdapterTest.php b/tests/Phinx/Db/Adapter/TablePrefixAdapterTest.php index 74f5749ac..1cd58824b 100644 --- a/tests/Phinx/Db/Adapter/TablePrefixAdapterTest.php +++ b/tests/Phinx/Db/Adapter/TablePrefixAdapterTest.php @@ -2,10 +2,20 @@ namespace Test\Phinx\Db\Adapter; +use Phinx\Db\Action\AddColumn; +use Phinx\Db\Action\AddForeignKey; +use Phinx\Db\Action\AddIndex; +use Phinx\Db\Action\ChangeColumn; +use Phinx\Db\Action\DropForeignKey; +use Phinx\Db\Action\DropIndex; +use Phinx\Db\Action\DropTable; +use Phinx\Db\Action\RemoveColumn; +use Phinx\Db\Action\RenameColumn; +use Phinx\Db\Action\RenameTable; use Phinx\Db\Adapter\TablePrefixAdapter; -use Phinx\Db\Table; use Phinx\Db\Table\Column; use Phinx\Db\Table\ForeignKey; +use Phinx\Db\Table\Table; use PHPUnit\Framework\TestCase; class TablePrefixAdapterTest extends TestCase @@ -285,58 +295,6 @@ public function testDropForeignKey() $this->adapter->dropForeignKey('table', $columns, $constraint); } - public function testAddTableWithForeignKey() - { - $this->mock - ->expects($this->any()) - ->method('isValidColumnType') - ->with($this->callback( - function ($column) { - return in_array($column->getType(), ['string', 'integer']); - } - )) - ->will($this->returnValue(true)); - - $table = new Table('table', [], $this->adapter); - $table - ->addColumn('bar', 'string') - ->addColumn('relation', 'integer') - ->addForeignKey('relation', 'target_table', ['id']); - - $this->mock - ->expects($this->once()) - ->method('createTable') - ->with($this->callback( - function ($table) { - if ($table->getName() !== 'pre_table_suf') { - throw new \Exception(sprintf( - 'Table::getName was not prefixed/suffixed properly: "%s"', - $table->getName() - )); - } - $fks = $table->getForeignKeys(); - if (count($fks) !== 1) { - throw new \Exception(sprintf( - 'Table::getForeignKeys count was incorrect: %d', - count($fks) - )); - } - foreach ($fks as $fk) { - if ($fk->getReferencedTable()->getName() !== 'pre_target_table_suf') { - throw new \Exception(sprintf( - 'ForeignKey::getReferencedTable was not prefixed/suffixed properly: "%s"', - $fk->getReferencedTable->getName() - )); - } - } - - return true; - } - )); - - $table->create(); - } - public function testInsertData() { $row = ['column1' => 'value3']; @@ -351,8 +309,50 @@ function ($table) { $this->equalTo($row) )); - $table = new Table('table', [], $this->adapter); + $table = new \Phinx\Db\Table('table', [], $this->adapter); $table->insert($row) ->save(); } + + public function actionsProvider() + { + $table = new Table('my_test'); + + return [ + [AddColumn::build($table, 'acolumn')], + [AddIndex::build($table, ['acolumn'])], + [AddForeignKey::build($table, ['acolumn'], 'another_table'), true], + [ChangeColumn::build($table, 'acolumn')], + [DropForeignKey::build($table, ['acolumn'])], + [DropIndex::build($table, ['acolumn'])], + [new DropTable($table)], + [RemoveColumn::build($table, 'acolumn')], + [RenameColumn::build($table, 'acolumn', 'another')], + [new RenameTable($table, 'new_name')], + ]; + } + + /** + * @dataProvider actionsProvider + */ + public function testExecuteActions($action, $checkReferecedTable = false) + { + $this->mock->expects($this->once()) + ->method('executeActions') + ->will($this->returnCallback(function ($table, $newActions) use ($action, $checkReferecedTable) { + $this->assertCount(1, $newActions); + $this->assertSame(get_class($action), get_class($newActions[0])); + $this->assertEquals('pre_my_test_suf', $newActions[0]->getTable()->getName()); + + if ($checkReferecedTable) { + $this->assertEquals( + 'pre_another_table_suf', + $newActions[0]->getForeignKey()->getReferencedTable()->getName() + ); + } + })); + + $table = new Table('my_test'); + $this->adapter->executeActions($table, [$action]); + } } diff --git a/tests/Phinx/Db/TableTest.php b/tests/Phinx/Db/TableTest.php index c31250ade..8149d7aea 100644 --- a/tests/Phinx/Db/TableTest.php +++ b/tests/Phinx/Db/TableTest.php @@ -64,9 +64,9 @@ public function testAddColumnWithColumnObject() ->setType('integer'); $table = new \Phinx\Db\Table('ntable', [], $adapter); $table->addColumn($column); - $columns = $table->getPendingColumns(); - $this->assertEquals('email', $columns[0]->getName()); - $this->assertEquals('integer', $columns[0]->getType()); + $actions = $this->getPendingActions($table); + $this->assertInstanceOf('Phinx\Db\Action\AddColumn', $actions[0]); + $this->assertSame($column, $actions[0]->getColumn()); } public function testAddColumnWithNoAdapterSpecified() @@ -81,7 +81,6 @@ public function testAddColumnWithNoAdapterSpecified() $e, 'Expected exception of type RuntimeException, got ' . get_class($e) ); - $this->assertRegExp('/An adapter must be specified to add a column./', $e->getMessage()); } } @@ -93,29 +92,6 @@ public function testAddComment() $this->assertEquals('test comment', $options['comment']); } - public function testAddForeignKey() - { - $adapter = new MysqlAdapter([]); - $table = new \Phinx\Db\Table('ntable', [], $adapter); - $table->addForeignKey('test', 'testTable', 'testRef'); - $fks = $table->getForeignKeys(); - $this->assertCount(1, $fks); - $this->assertContains('test', $fks[0]->getColumns()); - $this->assertContains('testRef', $fks[0]->getReferencedColumns()); - $this->assertEquals('testTable', $fks[0]->getReferencedTable()->getName()); - } - - public function testAddIndex() - { - $adapter = new MysqlAdapter([]); - $table = new \Phinx\Db\Table('ntable', [], $adapter); - $table->addIndex(['email'], ['unique' => true, 'name' => 'myemailindex']); - $indexes = $table->getIndexes(); - $this->assertEquals(\Phinx\Db\Table\Index::UNIQUE, $indexes[0]->getType()); - $this->assertEquals('myemailindex', $indexes[0]->getName()); - $this->assertContains('email', $indexes[0]->getColumns()); - } - public function testAddIndexWithIndexObject() { $adapter = new MysqlAdapter([]); @@ -124,19 +100,9 @@ public function testAddIndexWithIndexObject() ->setColumns(['email']); $table = new \Phinx\Db\Table('ntable', [], $adapter); $table->addIndex($index); - $indexes = $table->getIndexes(); - $this->assertEquals(\Phinx\Db\Table\Index::INDEX, $indexes[0]->getType()); - $this->assertContains('email', $indexes[0]->getColumns()); - } - - public function testAddIndexWithoutType() - { - $adapter = new MysqlAdapter([]); - $table = new \Phinx\Db\Table('ntable', [], $adapter); - $table->addIndex(['email']); - $indexes = $table->getIndexes(); - $this->assertEquals(\Phinx\Db\Table\Index::INDEX, $indexes[0]->getType()); - $this->assertContains('email', $indexes[0]->getColumns()); + $actions = $this->getPendingActions($table); + $this->assertInstanceOf('Phinx\Db\Action\AddIndex', $actions[0]); + $this->assertSame($index, $actions[0]->getIndex()); } /** @@ -152,8 +118,13 @@ public function testAddTimestamps(AdapterInterface $adapter, $createdAtColumnNam { $table = new \Phinx\Db\Table('ntable', [], $adapter); $table->addTimestamps($createdAtColumnName, $updatedAtColumnName); + $actions = $this->getPendingActions($table); - $columns = $table->getPendingColumns(); + $columns = []; + + foreach ($actions as $action) { + $columns[] = $action->getColumn(); + } $this->assertEquals($expectedCreatedAtColumnName, $columns[0]->getName()); $this->assertEquals('timestamp', $columns[0]->getType()); @@ -167,56 +138,6 @@ public function testAddTimestamps(AdapterInterface $adapter, $createdAtColumnNam $this->assertNull($columns[1]->getDefault()); } - public function testChangeColumn() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('changeColumn'); - $newColumn = new \Phinx\Db\Table\Column(); - $table = new \Phinx\Db\Table('ntable', [], $adapterStub); - $table->changeColumn('test1', $newColumn); - } - - public function testChangeColumnWithoutAColumnObject() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('changeColumn'); - $table = new \Phinx\Db\Table('ntable', [], $adapterStub); - $table->changeColumn('test1', 'text', ['null' => false]); - } - - public function testDropForeignKey() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('dropForeignKey'); - $table = new \Phinx\Db\Table('ntable', [], $adapterStub); - $table->dropForeignKey('test'); - } - - public function testGetColumns() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('getColumns'); - - $table = new \Phinx\Db\Table('table1', [], $adapterStub); - $table->getColumns(); - } - public function testInsert() { $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') @@ -260,61 +181,13 @@ public function testInsertSaveData() $adapterStub->expects($this->exactly(1)) ->method('bulkinsert') - ->with($table, [$data[0], $data[1], $moreData[0], $moreData[1]]); + ->with($table->getTable(), [$data[0], $data[1], $moreData[0], $moreData[1]]); $table->insert($data) ->insert($moreData) ->save(); } - public function testRemoveColumn() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('dropColumn'); - $table = new \Phinx\Db\Table('ntable', [], $adapterStub); - $table->removeColumn('test'); - } - - public function testRemoveIndex() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('dropIndex'); - $table = new \Phinx\Db\Table('ntable', [], $adapterStub); - $table->removeIndex(['email']); - } - - public function testRemoveIndexByName() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('dropIndexByName'); - $table = new \Phinx\Db\Table('ntable', [], $adapterStub); - $table->removeIndexByName('emailindex'); - } - - public function testRenameColumn() - { - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('renameColumn'); - $table = new \Phinx\Db\Table('ntable', [], $adapterStub); - $table->renameColumn('test1', 'test2'); - } - public function testResetAfterAddingData() { $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\MysqlAdapter') @@ -326,4 +199,12 @@ public function testResetAfterAddingData() $table->insert($columns, $data)->save(); $this->assertEquals([], $table->getData()); } + + protected function getPendingActions($table) + { + $prop = new \ReflectionProperty(get_class($table), 'actions'); + $prop->setAccessible(true); + + return $prop->getValue($table)->getActions(); + } } diff --git a/tests/Phinx/Migration/AbstractMigrationTest.php b/tests/Phinx/Migration/AbstractMigrationTest.php index 230cc0922..9d35f2781 100644 --- a/tests/Phinx/Migration/AbstractMigrationTest.php +++ b/tests/Phinx/Migration/AbstractMigrationTest.php @@ -260,20 +260,4 @@ public function testTableMethod() $migrationStub->table('test_table') ); } - - public function testDropTableMethod() - { - // stub migration - $migrationStub = $this->getMockForAbstractClass('\Phinx\Migration\AbstractMigration', ['mockenv', 0]); - - // stub adapter - $adapterStub = $this->getMockBuilder('\Phinx\Db\Adapter\PdoAdapter') - ->setConstructorArgs([[]]) - ->getMock(); - $adapterStub->expects($this->once()) - ->method('dropTable'); - - $migrationStub->setAdapter($adapterStub); - $migrationStub->dropTable('test_table'); - } } diff --git a/tests/Phinx/Migration/ManagerTest.php b/tests/Phinx/Migration/ManagerTest.php index 9a0f207fb..8a3420953 100644 --- a/tests/Phinx/Migration/ManagerTest.php +++ b/tests/Phinx/Migration/ManagerTest.php @@ -5485,9 +5485,10 @@ public function testReversibleMigrationsWorkAsExpected() $this->assertFalse($adapter->hasTable('info')); $this->assertTrue($adapter->hasTable('statuses')); $this->assertTrue($adapter->hasTable('users')); - $this->assertTrue($adapter->hasTable('user_logins')); + $this->assertTrue($adapter->hasTable('just_logins')); + $this->assertFalse($adapter->hasTable('user_logins')); $this->assertTrue($adapter->hasColumn('users', 'biography')); - $this->assertTrue($adapter->hasForeignKey('user_logins', ['user_id'])); + $this->assertTrue($adapter->hasForeignKey('just_logins', ['user_id'])); $this->assertTrue($adapter->hasTable('change_direction_test')); $this->assertTrue($adapter->hasColumn('change_direction_test', 'subthing')); $this->assertEquals( @@ -5502,6 +5503,7 @@ public function testReversibleMigrationsWorkAsExpected() $this->assertTrue($adapter->hasTable('info')); $this->assertFalse($adapter->hasTable('statuses')); $this->assertFalse($adapter->hasTable('user_logins')); + $this->assertFalse($adapter->hasTable('just_logins')); $this->assertTrue($adapter->hasColumn('users', 'bio')); $this->assertFalse($adapter->hasForeignKey('user_logins', ['user_id'])); $this->assertFalse($adapter->hasTable('change_direction_test')); @@ -5576,9 +5578,10 @@ public function testReversibleMigrationsWorkAsExpectedWithMixedNamespace() $this->assertFalse($adapter->hasTable('info')); $this->assertTrue($adapter->hasTable('statuses')); $this->assertTrue($adapter->hasTable('users')); - $this->assertTrue($adapter->hasTable('user_logins')); + $this->assertFalse($adapter->hasTable('user_logins')); + $this->assertTrue($adapter->hasTable('just_logins')); $this->assertTrue($adapter->hasColumn('users', 'biography')); - $this->assertTrue($adapter->hasForeignKey('user_logins', ['user_id'])); + $this->assertTrue($adapter->hasForeignKey('just_logins', ['user_id'])); $this->assertFalse($adapter->hasTable('info_baz')); $this->assertTrue($adapter->hasTable('statuses_baz')); @@ -5601,6 +5604,7 @@ public function testReversibleMigrationsWorkAsExpectedWithMixedNamespace() $this->assertTrue($adapter->hasTable('info')); $this->assertFalse($adapter->hasTable('statuses')); $this->assertFalse($adapter->hasTable('user_logins')); + $this->assertFalse($adapter->hasTable('just_logins')); $this->assertTrue($adapter->hasColumn('users', 'bio')); $this->assertFalse($adapter->hasForeignKey('user_logins', ['user_id'])); diff --git a/tests/Phinx/Migration/_files/reversiblemigrations/20121224200649_rename_info_table_to_statuses_table.php b/tests/Phinx/Migration/_files/reversiblemigrations/20121224200649_rename_info_table_to_statuses_table.php index 58e8e5ab0..8e9a0cd04 100644 --- a/tests/Phinx/Migration/_files/reversiblemigrations/20121224200649_rename_info_table_to_statuses_table.php +++ b/tests/Phinx/Migration/_files/reversiblemigrations/20121224200649_rename_info_table_to_statuses_table.php @@ -11,7 +11,7 @@ public function change() { // users table $table = $this->table('info'); - $table->rename('statuses'); + $table->rename('statuses')->save(); } /** diff --git a/tests/Phinx/Migration/_files/reversiblemigrations/20121224200739_rename_bio_to_biography.php b/tests/Phinx/Migration/_files/reversiblemigrations/20121224200739_rename_bio_to_biography.php index f9166afb0..a666dbb30 100644 --- a/tests/Phinx/Migration/_files/reversiblemigrations/20121224200739_rename_bio_to_biography.php +++ b/tests/Phinx/Migration/_files/reversiblemigrations/20121224200739_rename_bio_to_biography.php @@ -11,7 +11,7 @@ public function change() { // users table $table = $this->table('users'); - $table->renameColumn('bio', 'biography'); + $table->renameColumn('bio', 'biography')->save(); } /** diff --git a/tests/Phinx/Migration/_files/reversiblemigrations/20180431121930_tricky_edge_case.php b/tests/Phinx/Migration/_files/reversiblemigrations/20180431121930_tricky_edge_case.php new file mode 100644 index 000000000..c505200f5 --- /dev/null +++ b/tests/Phinx/Migration/_files/reversiblemigrations/20180431121930_tricky_edge_case.php @@ -0,0 +1,20 @@ +table('user_logins'); + $table + ->rename('just_logins') + ->addColumn('thingy', 'string', [ + 'limit' => 12, + 'null' => true, + ]) + ->addColumn('thingy2', 'integer') + ->addIndex(['thingy']) + ->save(); + } +} diff --git a/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200649_rename_info_table_to_statuses_table.php b/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200649_rename_info_table_to_statuses_table.php index 34e068164..b91b81078 100644 --- a/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200649_rename_info_table_to_statuses_table.php +++ b/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200649_rename_info_table_to_statuses_table.php @@ -13,7 +13,7 @@ public function change() { // users table $table = $this->table('info_baz'); - $table->rename('statuses_baz'); + $table->rename('statuses_baz')->save(); } /** diff --git a/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200739_rename_bio_to_biography.php b/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200739_rename_bio_to_biography.php index 2a60d5db9..2600d35aa 100644 --- a/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200739_rename_bio_to_biography.php +++ b/tests/Phinx/Migration/_files_baz/reversiblemigrations/20151224200739_rename_bio_to_biography.php @@ -13,7 +13,7 @@ public function change() { // users table $table = $this->table('users_baz'); - $table->renameColumn('bio', 'biography'); + $table->renameColumn('bio', 'biography')->save(); } /** diff --git a/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200649_rename_info_table_to_statuses_table.php b/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200649_rename_info_table_to_statuses_table.php index fc095d100..3bfbd9498 100644 --- a/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200649_rename_info_table_to_statuses_table.php +++ b/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200649_rename_info_table_to_statuses_table.php @@ -13,7 +13,7 @@ public function change() { // users table $table = $this->table('info_foo_bar'); - $table->rename('statuses_foo_bar'); + $table->rename('statuses_foo_bar')->save(); } /** diff --git a/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200739_rename_bio_to_biography.php b/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200739_rename_bio_to_biography.php index d76736d0e..8ebe1da82 100644 --- a/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200739_rename_bio_to_biography.php +++ b/tests/Phinx/Migration/_files_foo_bar/reversiblemigrations/20161224200739_rename_bio_to_biography.php @@ -13,7 +13,7 @@ public function change() { // users table $table = $this->table('users_foo_bar'); - $table->renameColumn('bio', 'biography'); + $table->renameColumn('bio', 'biography')->save(); } /**