修复seeder错误;删除第三方的think迁移工具,在extend中重新实现;

This commit is contained in:
2023-09-20 15:35:20 +08:00
parent 0b23e0a940
commit 85f2f29b81
91 changed files with 20798 additions and 66 deletions

View File

@@ -0,0 +1,38 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Table;
abstract class Action
{
/**
* @var \Phinx\Db\Table\Table
*/
protected $table;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table the Table to apply the action to
*/
public function __construct(Table $table)
{
$this->table = $table;
}
/**
* The table this action will be applied to
*
* @return \Phinx\Db\Table\Table
*/
public function getTable(): Table
{
return $this->table;
}
}

View File

@@ -0,0 +1,62 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Table;
class AddColumn extends Action
{
/**
* The column to add
*
* @var \Phinx\Db\Table\Column
*/
protected $column;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to add the column to
* @param \Phinx\Db\Table\Column $column The column to add
*/
public function __construct(Table $table, Column $column)
{
parent::__construct($table);
$this->column = $column;
}
/**
* Returns a new AddColumn object after assembling the given commands
*
* @param \Phinx\Db\Table\Table $table The table to add the column to
* @param string $columnName The column name
* @param string|\Phinx\Util\Literal $type The column type
* @param array<string, mixed> $options The column options
* @return static
*/
public static function build(Table $table, string $columnName, $type = null, array $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 \Phinx\Db\Table\Column
*/
public function getColumn(): Column
{
return $this->column;
}
}

View File

@@ -0,0 +1,78 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Table;
class AddForeignKey extends Action
{
/**
* The foreign key to add
*
* @var \Phinx\Db\Table\ForeignKey
*/
protected $foreignKey;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to add the foreign key to
* @param \Phinx\Db\Table\ForeignKey $fk The foreign key to add
*/
public function __construct(Table $table, ForeignKey $fk)
{
parent::__construct($table);
$this->foreignKey = $fk;
}
/**
* Creates a new AddForeignKey object after building the foreign key with
* the passed attributes
*
* @param \Phinx\Db\Table\Table $table The table object to add the foreign key to
* @param string|string[] $columns The columns for the foreign key
* @param \Phinx\Db\Table\Table|string $referencedTable The table the foreign key references
* @param string|string[] $referencedColumns The columns in the referenced table
* @param array<string, mixed> $options Extra options for the foreign key
* @param string|null $name The name of the foreign key
* @return static
*/
public static function build(Table $table, $columns, $referencedTable, $referencedColumns = ['id'], array $options = [], ?string $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 \Phinx\Db\Table\ForeignKey
*/
public function getForeignKey(): ForeignKey
{
return $this->foreignKey;
}
}

View File

@@ -0,0 +1,67 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Index;
use Phinx\Db\Table\Table;
class AddIndex extends Action
{
/**
* The index to add to the table
*
* @var \Phinx\Db\Table\Index
*/
protected $index;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to add the index to
* @param \Phinx\Db\Table\Index $index The index to be added
*/
public function __construct(Table $table, Index $index)
{
parent::__construct($table);
$this->index = $index;
}
/**
* Creates a new AddIndex object after building the index object with the
* provided arguments
*
* @param \Phinx\Db\Table\Table $table The table to add the index to
* @param string|string[]|\Phinx\Db\Table\Index $columns The columns to index
* @param array<string, mixed> $options Additional options for the index creation
* @return static
*/
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();
$index->setColumns($columns);
$index->setOptions($options);
}
return new static($table, $index);
}
/**
* Returns the index to be added
*
* @return \Phinx\Db\Table\Index
*/
public function getIndex(): Index
{
return $this->index;
}
}

View File

@@ -0,0 +1,87 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Table;
class ChangeColumn extends Action
{
/**
* The column definition
*
* @var \Phinx\Db\Table\Column
*/
protected $column;
/**
* The name of the column to be changed
*
* @var string
*/
protected $columnName;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to alter
* @param string $columnName The name of the column to change
* @param \Phinx\Db\Table\Column $column The column definition
*/
public function __construct(Table $table, string $columnName, Column $column)
{
parent::__construct($table);
$this->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 \Phinx\Db\Table\Table $table The table to alter
* @param string $columnName The name of the column to change
* @param string|\Phinx\Db\Table\Column|\Phinx\Util\Literal $type The type of the column
* @param array<string, mixed> $options Additional options for the column
* @return static
*/
public static function build(Table $table, string $columnName, $type = null, array $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(): string
{
return $this->columnName;
}
/**
* Returns the column definition
*
* @return \Phinx\Db\Table\Column
*/
public function getColumn(): Column
{
return $this->column;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Table;
class ChangeComment extends Action
{
/**
* The new comment for the table
*
* @var string|null
*/
protected $newComment;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to be changed
* @param string|null $newComment The new comment for the table
*/
public function __construct(Table $table, ?string $newComment)
{
parent::__construct($table);
$this->newComment = $newComment;
}
/**
* Return the new comment for the table
*
* @return string|null
*/
public function getNewComment(): ?string
{
return $this->newComment;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Table;
class ChangePrimaryKey extends Action
{
/**
* The new columns for the primary key
*
* @var string|string[]|null
*/
protected $newColumns;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to be changed
* @param string|string[]|null $newColumns The new columns for the primary key
*/
public function __construct(Table $table, $newColumns)
{
parent::__construct($table);
$this->newColumns = $newColumns;
}
/**
* Return the new columns for the primary key
*
* @return string|string[]|null
*/
public function getNewColumns()
{
return $this->newColumns;
}
}

View File

@@ -0,0 +1,12 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
class CreateTable extends Action
{
}

View File

@@ -0,0 +1,68 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Table;
class DropForeignKey extends Action
{
/**
* The foreign key to remove
*
* @var \Phinx\Db\Table\ForeignKey
*/
protected $foreignKey;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to remove the constraint from
* @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to remove
*/
public function __construct(Table $table, ForeignKey $foreignKey)
{
parent::__construct($table);
$this->foreignKey = $foreignKey;
}
/**
* Creates a new DropForeignKey object after building the ForeignKey
* definition out of the passed arguments.
*
* @param \Phinx\Db\Table\Table $table The table to delete the foreign key from
* @param string|string[] $columns The columns participating in the foreign key
* @param string|null $constraint The constraint name
* @return static
*/
public static function build(Table $table, $columns, ?string $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 \Phinx\Db\Table\ForeignKey
*/
public function getForeignKey(): ForeignKey
{
return $this->foreignKey;
}
}

View File

@@ -0,0 +1,75 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Index;
use Phinx\Db\Table\Table;
class DropIndex extends Action
{
/**
* The index to drop
*
* @var \Phinx\Db\Table\Index
*/
protected $index;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table owning the index
* @param \Phinx\Db\Table\Index $index The index to be dropped
*/
public function __construct(Table $table, Index $index)
{
parent::__construct($table);
$this->index = $index;
}
/**
* Creates a new DropIndex object after assembling the passed
* arguments.
*
* @param \Phinx\Db\Table\Table $table The table where the index is
* @param string[] $columns the indexed columns
* @return static
*/
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 known.
*
* @param \Phinx\Db\Table\Table $table The table where the index is
* @param string $name The name of the index
* @return static
*/
public static function buildFromName(Table $table, string $name)
{
$index = new Index();
$index->setName($name);
return new static($table, $index);
}
/**
* Returns the index to be dropped
*
* @return \Phinx\Db\Table\Index
*/
public function getIndex(): Index
{
return $this->index;
}
}

View File

@@ -0,0 +1,12 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
class DropTable extends Action
{
}

View File

@@ -0,0 +1,59 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Table;
class RemoveColumn extends Action
{
/**
* The column to be removed
*
* @var \Phinx\Db\Table\Column
*/
protected $column;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table where the column is
* @param \Phinx\Db\Table\Column $column The column to be removed
*/
public function __construct(Table $table, Column $column)
{
parent::__construct($table);
$this->column = $column;
}
/**
* Creates a new RemoveColumn object after assembling the
* passed arguments.
*
* @param \Phinx\Db\Table\Table $table The table where the column is
* @param string $columnName The name of the column to drop
* @return static
*/
public static function build(Table $table, string $columnName)
{
$column = new Column();
$column->setName($columnName);
return new static($table, $column);
}
/**
* Returns the column to be dropped
*
* @return \Phinx\Db\Table\Column
*/
public function getColumn(): Column
{
return $this->column;
}
}

View File

@@ -0,0 +1,79 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Table;
class RenameColumn extends Action
{
/**
* The column to be renamed
*
* @var \Phinx\Db\Table\Column
*/
protected $column;
/**
* The new name for the column
*
* @var string
*/
protected $newName;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table where the column is
* @param \Phinx\Db\Table\Column $column The column to be renamed
* @param string $newName The new name for the column
*/
public function __construct(Table $table, Column $column, string $newName)
{
parent::__construct($table);
$this->newName = $newName;
$this->column = $column;
}
/**
* Creates a new RenameColumn object after building the passed
* arguments
*
* @param \Phinx\Db\Table\Table $table The table where the column is
* @param string $columnName The name of the column to be changed
* @param string $newName The new name for the column
* @return static
*/
public static function build(Table $table, string $columnName, string $newName)
{
$column = new Column();
$column->setName($columnName);
return new static($table, $column, $newName);
}
/**
* Returns the column to be changed
*
* @return \Phinx\Db\Table\Column
*/
public function getColumn(): Column
{
return $this->column;
}
/**
* Returns the new name for the column
*
* @return string
*/
public function getNewName(): string
{
return $this->newName;
}
}

View File

@@ -0,0 +1,42 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Action;
use Phinx\Db\Table\Table;
class RenameTable extends Action
{
/**
* The new name for the table
*
* @var string
*/
protected $newName;
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to be renamed
* @param string $newName The new name for the table
*/
public function __construct(Table $table, string $newName)
{
parent::__construct($table);
$this->newName = $newName;
}
/**
* Return the new name for the table
*
* @return string
*/
public function getNewName(): string
{
return $this->newName;
}
}

View File

@@ -0,0 +1,412 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use Exception;
use InvalidArgumentException;
use Phinx\Db\Table;
use Phinx\Db\Table\Column;
use Phinx\Util\Literal;
use think\console\Input as InputInterface;
use think\migration\NullOutput;
use think\console\Output as OutputInterface;
/**
* Base Abstract Database Adapter.
*/
abstract class AbstractAdapter implements AdapterInterface
{
/**
* @var array<string, mixed>
*/
protected $options = [];
/**
* @var \think\console\Input|null
*/
protected $input;
/**
* @var \think\console\Output
*/
protected $output;
/**
* @var string[]
*/
protected $createdTables = [];
/**
* @var string
*/
protected $schemaTableName = 'phinxlog';
/**
* @var array
*/
protected $dataDomain = [];
/**
* Class Constructor.
*
* @param array<string, mixed> $options Options
* @param \think\console\Input|null $input Input Interface
* @param \think\console\Output|null $output Output Interface
*/
public function __construct(array $options, ?InputInterface $input = null, ?OutputInterface $output = null)
{
$this->setOptions($options);
if ($input !== null) {
$this->setInput($input);
}
if ($output !== null) {
$this->setOutput($output);
}
}
/**
* @inheritDoc
*/
public function setOptions(array $options): AdapterInterface
{
$this->options = $options;
if (isset($options['default_migration_table'])) {
trigger_error('The default_migration_table setting for adapter has been deprecated since 0.13.0. Use `migration_table` instead.', E_USER_DEPRECATED);
if (!isset($options['migration_table'])) {
$options['migration_table'] = $options['default_migration_table'];
}
}
if (isset($options['migration_table'])) {
$this->setSchemaTableName($options['migration_table']);
}
if (isset($options['data_domain'])) {
$this->setDataDomain($options['data_domain']);
}
return $this;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->options;
}
/**
* @inheritDoc
*/
public function hasOption(string $name): bool
{
return isset($this->options[$name]);
}
/**
* @inheritDoc
*/
public function getOption(string $name)
{
if (!$this->hasOption($name)) {
return null;
}
return $this->options[$name];
}
/**
* @inheritDoc
*/
public function setInput(InputInterface $input): AdapterInterface
{
$this->input = $input;
return $this;
}
/**
* @inheritDoc
*/
public function getInput(): ?InputInterface
{
return $this->input;
}
/**
* @inheritDoc
*/
public function setOutput(OutputInterface $output): AdapterInterface
{
$this->output = $output;
return $this;
}
/**
* @inheritDoc
*/
public function getOutput(): OutputInterface
{
if ($this->output === null) {
$output = new NullOutput();
$this->setOutput($output);
}
return $this->output;
}
/**
* @inheritDoc
* @return array<int>
*/
public function getVersions(): array
{
$rows = $this->getVersionLog();
return array_keys($rows);
}
/**
* Gets the schema table name.
*
* @return string
*/
public function getSchemaTableName(): string
{
return $this->schemaTableName;
}
/**
* Sets the schema table name.
*
* @param string $schemaTableName Schema Table Name
* @return $this
*/
public function setSchemaTableName(string $schemaTableName)
{
$this->schemaTableName = $schemaTableName;
return $this;
}
/**
* Gets the data domain.
*
* @return array
*/
public function getDataDomain(): array
{
return $this->dataDomain;
}
/**
* Sets the data domain.
*
* @param array $dataDomain Array for the data domain
* @return $this
*/
public function setDataDomain(array $dataDomain)
{
$this->dataDomain = [];
// Iterate over data domain field definitions and perform initial and
// simple normalization. We make sure the definition as a base 'type'
// and it is compatible with the base Phinx types.
foreach ($dataDomain as $type => $options) {
if (!isset($options['type'])) {
throw new \InvalidArgumentException(sprintf(
'You must specify a type for data domain type "%s".',
$type
));
}
// Replace type if it's the name of a Phinx constant
if (defined('static::' . $options['type'])) {
$options['type'] = constant('static::' . $options['type']);
}
if (!in_array($options['type'], $this->getColumnTypes(), true)) {
throw new \InvalidArgumentException(sprintf(
'An invalid column type "%s" was specified for data domain type "%s".',
$options['type'],
$type
));
}
$internal_type = $options['type'];
unset($options['type']);
// Do a simple replacement for the 'length' / 'limit' option and
// detect hinting values for 'limit'.
if (isset($options['length'])) {
$options['limit'] = $options['length'];
unset($options['length']);
}
if (isset($options['limit']) && !is_numeric($options['limit'])) {
if (!defined('static::' . $options['limit'])) {
throw new \InvalidArgumentException(sprintf(
'An invalid limit value "%s" was specified for data domain type "%s".',
$options['limit'],
$type
));
}
$options['limit'] = constant('static::' . $options['limit']);
}
// Save the data domain types in a more suitable format
$this->dataDomain[$type] = [
'type' => $internal_type,
'options' => $options,
];
}
return $this;
}
/**
* @inheritdoc
*/
public function getColumnForType(string $columnName, string $type, array $options): Column
{
$column = new Column();
$column->setName($columnName);
if (array_key_exists($type, $this->getDataDomain())) {
$column->setType($this->dataDomain[$type]['type']);
$column->setOptions($this->dataDomain[$type]['options']);
} else {
$column->setType($type);
}
$column->setOptions($options);
return $column;
}
/**
* @inheritDoc
* @throws \InvalidArgumentException
* @return void
*/
public function createSchemaTable(): void
{
try {
$options = [
'id' => false,
'primary_key' => 'version',
];
$table = new Table($this->getSchemaTableName(), $options, $this);
$table->addColumn('version', 'biginteger', ['null' => false])
->addColumn('migration_name', 'string', ['limit' => 100, 'default' => null, 'null' => true])
->addColumn('start_time', 'timestamp', ['default' => null, 'null' => true])
->addColumn('end_time', 'timestamp', ['default' => null, 'null' => true])
->addColumn('breakpoint', 'boolean', ['default' => false, 'null' => false])
->save();
} catch (Exception $exception) {
throw new InvalidArgumentException(
'There was a problem creating the schema table: ' . $exception->getMessage(),
(int)$exception->getCode(),
$exception
);
}
}
/**
* @inheritDoc
*/
public function getAdapterType(): string
{
return $this->getOption('adapter');
}
/**
* @inheritDoc
*/
public function isValidColumnType(Column $column): bool
{
return $column->getType() instanceof Literal || in_array($column->getType(), $this->getColumnTypes(), true);
}
/**
* Determines if instead of executing queries a dump to standard output is needed
*
* @return bool
*/
public function isDryRunEnabled(): bool
{
/** @var \think\console\Input|null $input */
$input = $this->getInput();
return $input && $input->hasOption('dry-run') ? (bool)$input->getOption('dry-run') : false;
}
/**
* Adds user-created tables (e.g. not phinxlog) to a cached list
*
* @param string $tableName The name of the table
* @return void
*/
protected function addCreatedTable(string $tableName): void
{
$tableName = $this->quoteTableName($tableName);
if (substr_compare($tableName, 'phinxlog', -strlen('phinxlog')) !== 0) {
$this->createdTables[] = $tableName;
}
}
/**
* Updates the name of the cached table
*
* @param string $tableName Original name of the table
* @param string $newTableName New name of the table
* @return void
*/
protected function updateCreatedTableName(string $tableName, string $newTableName): void
{
$tableName = $this->quoteTableName($tableName);
$newTableName = $this->quoteTableName($newTableName);
$key = array_search($tableName, $this->createdTables, true);
if ($key !== false) {
$this->createdTables[$key] = $newTableName;
}
}
/**
* Removes table from the cached created list
*
* @param string $tableName The name of the table
* @return void
*/
protected function removeCreatedTable(string $tableName): void
{
$tableName = $this->quoteTableName($tableName);
$key = array_search($tableName, $this->createdTables, true);
if ($key !== false) {
unset($this->createdTables[$key]);
}
}
/**
* Check if the table is in the cached list of created tables
*
* @param string $tableName The name of the table
* @return bool
*/
protected function hasCreatedTable(string $tableName): bool
{
$tableName = $this->quoteTableName($tableName);
return in_array($tableName, $this->createdTables, true);
}
}

View File

@@ -0,0 +1,172 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use RuntimeException;
/**
* Adapter factory and registry.
*
* Used for registering adapters and creating instances of adapters.
*
* @author Woody Gilk <woody.gilk@gmail.com>
*/
class AdapterFactory
{
/**
* @var \Phinx\Db\Adapter\AdapterFactory|null
*/
protected static $instance;
/**
* Get the factory singleton instance.
*
* @return \Phinx\Db\Adapter\AdapterFactory
*/
public static function instance()
{
if (!static::$instance) {
static::$instance = new static();
}
return static::$instance;
}
/**
* Class map of database adapters, indexed by PDO::ATTR_DRIVER_NAME.
*
* @var array<string, \Phinx\Db\Adapter\AdapterInterface|string>
* @phpstan-var array<string, \Phinx\Db\Adapter\AdapterInterface|class-string<\Phinx\Db\Adapter\AdapterInterface>>
*/
protected $adapters = [
'mysql' => 'Phinx\Db\Adapter\MysqlAdapter',
'pgsql' => 'Phinx\Db\Adapter\PostgresAdapter',
'sqlite' => 'Phinx\Db\Adapter\SQLiteAdapter',
'sqlsrv' => 'Phinx\Db\Adapter\SqlServerAdapter',
];
/**
* Class map of adapters wrappers, indexed by name.
*
* @var array<string, \Phinx\Db\Adapter\WrapperInterface|string>
*/
protected $wrappers = [
'prefix' => 'Phinx\Db\Adapter\TablePrefixAdapter',
'proxy' => 'Phinx\Db\Adapter\ProxyAdapter',
'timed' => 'Phinx\Db\Adapter\TimedOutputAdapter',
];
/**
* Register an adapter class with a given name.
*
* @param string $name Name
* @param object|string $class Class
* @throws \RuntimeException
* @return $this
*/
public function registerAdapter(string $name, $class)
{
if (!is_subclass_of($class, 'Phinx\Db\Adapter\AdapterInterface')) {
throw new RuntimeException(sprintf(
'Adapter class "%s" must implement Phinx\\Db\\Adapter\\AdapterInterface',
is_string($class) ? $class : get_class($class)
));
}
$this->adapters[$name] = $class;
return $this;
}
/**
* Get an adapter class by name.
*
* @param string $name Name
* @throws \RuntimeException
* @return object|string
* @phpstan-return object|class-string<\Phinx\Db\Adapter\AdapterInterface>
*/
protected function getClass(string $name)
{
if (empty($this->adapters[$name])) {
throw new RuntimeException(sprintf(
'Adapter "%s" has not been registered',
$name
));
}
return $this->adapters[$name];
}
/**
* Get an adapter instance by name.
*
* @param string $name Name
* @param array<string, mixed> $options Options
* @return \Phinx\Db\Adapter\AdapterInterface
*/
public function getAdapter(string $name, array $options): AdapterInterface
{
$class = $this->getClass($name);
return new $class($options);
}
/**
* Add or replace a wrapper with a fully qualified class name.
*
* @param string $name Name
* @param object|string $class Class
* @throws \RuntimeException
* @return $this
*/
public function registerWrapper(string $name, $class)
{
if (!is_subclass_of($class, 'Phinx\Db\Adapter\WrapperInterface')) {
throw new RuntimeException(sprintf(
'Wrapper class "%s" must be implement Phinx\\Db\\Adapter\\WrapperInterface',
is_string($class) ? $class : get_class($class)
));
}
$this->wrappers[$name] = $class;
return $this;
}
/**
* Get a wrapper class by name.
*
* @param string $name Name
* @throws \RuntimeException
* @return \Phinx\Db\Adapter\WrapperInterface|string
*/
protected function getWrapperClass(string $name)
{
if (empty($this->wrappers[$name])) {
throw new RuntimeException(sprintf(
'Wrapper "%s" has not been registered',
$name
));
}
return $this->wrappers[$name];
}
/**
* Get a wrapper instance by name.
*
* @param string $name Name
* @param \Phinx\Db\Adapter\AdapterInterface $adapter Adapter
* @return \Phinx\Db\Adapter\AdapterWrapper
*/
public function getWrapper(string $name, AdapterInterface $adapter): AdapterWrapper
{
$class = $this->getWrapperClass($name);
return new $class($adapter);
}
}

View File

@@ -0,0 +1,505 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use Cake\Database\Query;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Table;
use Phinx\Migration\MigrationInterface;
use think\console\Input as InputInterface;
use think\console\Output as OutputInterface;
/**
* Adapter Interface.
*
* @author Rob Morgan <robbym@gmail.com>
* @method \PDO getConnection()
*/
interface AdapterInterface
{
public const PHINX_TYPE_STRING = 'string';
public const PHINX_TYPE_CHAR = 'char';
public const PHINX_TYPE_TEXT = 'text';
public const PHINX_TYPE_INTEGER = 'integer';
public const PHINX_TYPE_TINY_INTEGER = 'tinyinteger';
public const PHINX_TYPE_SMALL_INTEGER = 'smallinteger';
public const PHINX_TYPE_BIG_INTEGER = 'biginteger';
public const PHINX_TYPE_BIT = 'bit';
public const PHINX_TYPE_FLOAT = 'float';
public const PHINX_TYPE_DECIMAL = 'decimal';
public const PHINX_TYPE_DOUBLE = 'double';
public const PHINX_TYPE_DATETIME = 'datetime';
public const PHINX_TYPE_TIMESTAMP = 'timestamp';
public const PHINX_TYPE_TIME = 'time';
public const PHINX_TYPE_DATE = 'date';
public const PHINX_TYPE_BINARY = 'binary';
public const PHINX_TYPE_VARBINARY = 'varbinary';
public const PHINX_TYPE_BINARYUUID = 'binaryuuid';
public const PHINX_TYPE_BLOB = 'blob';
public const PHINX_TYPE_TINYBLOB = 'tinyblob'; // Specific to Mysql.
public const PHINX_TYPE_MEDIUMBLOB = 'mediumblob'; // Specific to Mysql
public const PHINX_TYPE_LONGBLOB = 'longblob'; // Specific to Mysql
public const PHINX_TYPE_BOOLEAN = 'boolean';
public const PHINX_TYPE_JSON = 'json';
public const PHINX_TYPE_JSONB = 'jsonb';
public const PHINX_TYPE_UUID = 'uuid';
public const PHINX_TYPE_FILESTREAM = 'filestream';
// Geospatial database types
public const PHINX_TYPE_GEOMETRY = 'geometry';
public const PHINX_TYPE_GEOGRAPHY = 'geography';
public const PHINX_TYPE_POINT = 'point';
public const PHINX_TYPE_LINESTRING = 'linestring';
public const PHINX_TYPE_POLYGON = 'polygon';
public const PHINX_TYPES_GEOSPATIAL = [
self::PHINX_TYPE_GEOMETRY,
self::PHINX_TYPE_POINT,
self::PHINX_TYPE_LINESTRING,
self::PHINX_TYPE_POLYGON,
];
// only for mysql so far
public const PHINX_TYPE_MEDIUM_INTEGER = 'mediuminteger';
public const PHINX_TYPE_ENUM = 'enum';
public const PHINX_TYPE_SET = 'set';
public const PHINX_TYPE_YEAR = 'year';
// only for postgresql so far
public const PHINX_TYPE_CIDR = 'cidr';
public const PHINX_TYPE_INET = 'inet';
public const PHINX_TYPE_MACADDR = 'macaddr';
public const PHINX_TYPE_INTERVAL = 'interval';
/**
* Get all migrated version numbers.
*
* @return array<int>
*/
public function getVersions(): array;
/**
* Get all migration log entries, indexed by version creation time and sorted ascendingly by the configuration's
* version order option
*
* @return array<int, mixed>
*/
public function getVersionLog(): array;
/**
* Set adapter configuration options.
*
* @param array<string, mixed> $options Options
* @return $this
*/
public function setOptions(array $options);
/**
* Get all adapter options.
*
* @return array<string, mixed>
*/
public function getOptions(): array;
/**
* Check if an option has been set.
*
* @param string $name Name
* @return bool
*/
public function hasOption(string $name): bool;
/**
* Get a single adapter option, or null if the option does not exist.
*
* @param string $name Name
* @return mixed
*/
public function getOption(string $name);
/**
* Sets the console input.
*
* @param \think\console\Input $input Input
* @return $this
*/
public function setInput(InputInterface $input);
/**
* Gets the console input.
*
* @return \think\console\Input|null
*/
public function getInput(): ?InputInterface;
/**
* Sets the console output.
*
* @param \think\console\Output $output Output
* @return $this
*/
public function setOutput(OutputInterface $output);
/**
* Gets the console output.
*
* @return \think\console\Output
*/
public function getOutput(): OutputInterface;
/**
* Returns a new Phinx\Db\Table\Column using the existent data domain.
*
* @param string $columnName The desired column name
* @param string $type The type for the column. Can be a data domain type.
* @param array<string, mixed> $options Options array
* @return \Phinx\Db\Table\Column
*/
public function getColumnForType(string $columnName, string $type, array $options): Column;
/**
* Records a migration being run.
*
* @param \Phinx\Migration\MigrationInterface $migration Migration
* @param string $direction Direction
* @param string $startTime Start Time
* @param string $endTime End Time
* @return $this
*/
public function migrated(MigrationInterface $migration, string $direction, string $startTime, string $endTime);
/**
* Toggle a migration breakpoint.
*
* @param \Phinx\Migration\MigrationInterface $migration Migration
* @return $this
*/
public function toggleBreakpoint(MigrationInterface $migration);
/**
* Reset all migration breakpoints.
*
* @return int The number of breakpoints reset
*/
public function resetAllBreakpoints(): int;
/**
* Set a migration breakpoint.
*
* @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint set
* @return $this
*/
public function setBreakpoint(MigrationInterface $migration);
/**
* Unset a migration breakpoint.
*
* @param \Phinx\Migration\MigrationInterface $migration The migration target for the breakpoint unset
* @return $this
*/
public function unsetBreakpoint(MigrationInterface $migration);
/**
* Creates the schema table.
*
* @return void
*/
public function createSchemaTable(): void;
/**
* Returns the adapter type.
*
* @return string
*/
public function getAdapterType(): string;
/**
* Initializes the database connection.
*
* @throws \RuntimeException When the requested database driver is not installed.
* @return void
*/
public function connect(): void;
/**
* Closes the database connection.
*
* @return void
*/
public function disconnect(): void;
/**
* Does the adapter support transactions?
*
* @return bool
*/
public function hasTransactions(): bool;
/**
* Begin a transaction.
*
* @return void
*/
public function beginTransaction(): void;
/**
* Commit a transaction.
*
* @return void
*/
public function commitTransaction(): void;
/**
* Rollback a transaction.
*
* @return void
*/
public function rollbackTransaction(): void;
/**
* Executes a SQL statement and returns the number of affected rows.
*
* @param string $sql SQL
* @param array $params parameters to use for prepared query
* @return int
*/
public function execute(string $sql, array $params = []): int;
/**
* 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): void;
/**
* Returns a new Query object
*
* @return \Cake\Database\Query
*/
public function getQueryBuilder(): Query;
/**
* Executes a SQL statement.
*
* The return type depends on the underlying adapter being used.
*
* @param string $sql SQL
* @param array $params parameters to use for prepared query
* @return mixed
*/
public function query(string $sql, array $params = []);
/**
* Executes a query and returns only one row as an array.
*
* @param string $sql SQL
* @return array|false
*/
public function fetchRow(string $sql);
/**
* Executes a query and returns an array of rows.
*
* @param string $sql SQL
* @return array
*/
public function fetchAll(string $sql): array;
/**
* Inserts data into a table.
*
* @param \Phinx\Db\Table\Table $table Table where to insert data
* @param array $row Row
* @return void
*/
public function insert(Table $table, array $row): void;
/**
* Inserts data into a table in a bulk.
*
* @param \Phinx\Db\Table\Table $table Table where to insert data
* @param array $rows Rows
* @return void
*/
public function bulkinsert(Table $table, array $rows): void;
/**
* Quotes a table name for use in a query.
*
* @param string $tableName Table name
* @return string
*/
public function quoteTableName(string $tableName): string;
/**
* Quotes a column name for use in a query.
*
* @param string $columnName Table name
* @return string
*/
public function quoteColumnName(string $columnName): string;
/**
* Checks to see if a table exists.
*
* @param string $tableName Table name
* @return bool
*/
public function hasTable(string $tableName): bool;
/**
* Creates the specified database 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, array $columns = [], array $indexes = []): void;
/**
* Truncates the specified table
*
* @param string $tableName Table name
* @return void
*/
public function truncateTable(string $tableName): void;
/**
* Returns table columns
*
* @param string $tableName Table name
* @return \Phinx\Db\Table\Column[]
*/
public function getColumns(string $tableName): array;
/**
* Checks to see if a column exists.
*
* @param string $tableName Table name
* @param string $columnName Column name
* @return bool
*/
public function hasColumn(string $tableName, string $columnName): bool;
/**
* Checks to see if an index exists.
*
* @param string $tableName Table name
* @param string|string[] $columns Column(s)
* @return bool
*/
public function hasIndex(string $tableName, $columns): bool;
/**
* Checks to see if an index specified by name exists.
*
* @param string $tableName Table name
* @param string $indexName Index name
* @return bool
*/
public function hasIndexByName(string $tableName, string $indexName): bool;
/**
* Checks to see if the specified primary key exists.
*
* @param string $tableName Table name
* @param string|string[] $columns Column(s)
* @param string|null $constraint Constraint name
* @return bool
*/
public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool;
/**
* Checks to see if a foreign key exists.
*
* @param string $tableName Table name
* @param string|string[] $columns Column(s)
* @param string|null $constraint Constraint name
* @return bool
*/
public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool;
/**
* Returns an array of the supported Phinx column types.
*
* @return string[]
*/
public function getColumnTypes(): array;
/**
* Checks that the given column is of a supported type.
*
* @param \Phinx\Db\Table\Column $column Column
* @return bool
*/
public function isValidColumnType(Column $column): bool;
/**
* Converts the Phinx logical type to the adapter's SQL type.
*
* @param \Phinx\Util\Literal|string $type Type
* @param int|null $limit Limit
* @return array
*/
public function getSqlType($type, ?int $limit = null): array;
/**
* Creates a new database.
*
* @param string $name Database Name
* @param array<string, mixed> $options Options
* @return void
*/
public function createDatabase(string $name, array $options = []): void;
/**
* Checks to see if a database exists.
*
* @param string $name Database Name
* @return bool
*/
public function hasDatabase(string $name): bool;
/**
* Drops the specified database.
*
* @param string $name Database Name
* @return void
*/
public function dropDatabase(string $name): void;
/**
* Creates the specified schema or throws an exception
* if there is no support for it.
*
* @param string $schemaName Schema Name
* @return void
*/
public function createSchema(string $schemaName = 'public'): void;
/**
* Drops the specified schema table or throws an exception
* if there is no support for it.
*
* @param string $schemaName Schema name
* @return void
*/
public function dropSchema(string $schemaName): void;
/**
* Cast a value to a boolean appropriate for the adapter.
*
* @param mixed $value The value to be cast
* @return mixed
*/
public function castToBool($value);
}

View File

@@ -0,0 +1,487 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use Cake\Database\Query;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Table;
use Phinx\Migration\MigrationInterface;
use think\console\Input as InputInterface;
use think\console\Output as OutputInterface;
/**
* Adapter Wrapper.
*
* Proxy commands through to another adapter, allowing modification of
* parameters during calls.
*
* @author Woody Gilk <woody.gilk@gmail.com>
*/
abstract class AdapterWrapper implements AdapterInterface, WrapperInterface
{
/**
* @var \Phinx\Db\Adapter\AdapterInterface
*/
protected $adapter;
/**
* @inheritDoc
*/
public function __construct(AdapterInterface $adapter)
{
$this->setAdapter($adapter);
}
/**
* @inheritDoc
*/
public function setAdapter(AdapterInterface $adapter): AdapterInterface
{
$this->adapter = $adapter;
return $this;
}
/**
* @inheritDoc
*/
public function getAdapter(): AdapterInterface
{
return $this->adapter;
}
/**
* @inheritDoc
*/
public function setOptions(array $options): AdapterInterface
{
$this->adapter->setOptions($options);
return $this;
}
/**
* @inheritDoc
*/
public function getOptions(): array
{
return $this->adapter->getOptions();
}
/**
* @inheritDoc
*/
public function hasOption(string $name): bool
{
return $this->adapter->hasOption($name);
}
/**
* @inheritDoc
*/
public function getOption(string $name)
{
return $this->adapter->getOption($name);
}
/**
* @inheritDoc
*/
public function setInput(InputInterface $input): AdapterInterface
{
$this->adapter->setInput($input);
return $this;
}
/**
* @inheritDoc
*/
public function getInput(): InputInterface
{
return $this->adapter->getInput();
}
/**
* @inheritDoc
*/
public function setOutput(OutputInterface $output): AdapterInterface
{
$this->adapter->setOutput($output);
return $this;
}
/**
* @inheritDoc
*/
public function getOutput(): OutputInterface
{
return $this->adapter->getOutput();
}
/**
* @inheritDoc
*/
public function getColumnForType(string $columnName, string $type, array $options): Column
{
return $this->adapter->getColumnForType($columnName, $type, $options);
}
/**
* @inheritDoc
*/
public function connect(): void
{
$this->getAdapter()->connect();
}
/**
* @inheritDoc
*/
public function disconnect(): void
{
$this->getAdapter()->disconnect();
}
/**
* @inheritDoc
*/
public function execute(string $sql, array $params = []): int
{
return $this->getAdapter()->execute($sql, $params);
}
/**
* @inheritDoc
*/
public function query(string $sql, array $params = [])
{
return $this->getAdapter()->query($sql, $params);
}
/**
* @inheritDoc
*/
public function insert(Table $table, array $row): void
{
$this->getAdapter()->insert($table, $row);
}
/**
* @inheritDoc
*/
public function bulkinsert(Table $table, array $rows): void
{
$this->getAdapter()->bulkinsert($table, $rows);
}
/**
* @inheritDoc
*/
public function fetchRow(string $sql)
{
return $this->getAdapter()->fetchRow($sql);
}
/**
* @inheritDoc
*/
public function fetchAll(string $sql): array
{
return $this->getAdapter()->fetchAll($sql);
}
/**
* @inheritDoc
*/
public function getVersions(): array
{
return $this->getAdapter()->getVersions();
}
/**
* @inheritDoc
*/
public function getVersionLog(): array
{
return $this->getAdapter()->getVersionLog();
}
/**
* @inheritDoc
*/
public function migrated(MigrationInterface $migration, string $direction, string $startTime, string $endTime): AdapterInterface
{
$this->getAdapter()->migrated($migration, $direction, $startTime, $endTime);
return $this;
}
/**
* @inheritDoc
*/
public function toggleBreakpoint(MigrationInterface $migration): AdapterInterface
{
$this->getAdapter()->toggleBreakpoint($migration);
return $this;
}
/**
* @inheritDoc
*/
public function resetAllBreakpoints(): int
{
return $this->getAdapter()->resetAllBreakpoints();
}
/**
* @inheritDoc
*/
public function setBreakpoint(MigrationInterface $migration): AdapterInterface
{
$this->getAdapter()->setBreakpoint($migration);
return $this;
}
/**
* @inheritDoc
*/
public function unsetBreakpoint(MigrationInterface $migration): AdapterInterface
{
$this->getAdapter()->unsetBreakpoint($migration);
return $this;
}
/**
* @inheritDoc
*/
public function createSchemaTable(): void
{
$this->getAdapter()->createSchemaTable();
}
/**
* @inheritDoc
*/
public function getColumnTypes(): array
{
return $this->getAdapter()->getColumnTypes();
}
/**
* @inheritDoc
*/
public function isValidColumnType(Column $column): bool
{
return $this->getAdapter()->isValidColumnType($column);
}
/**
* @inheritDoc
*/
public function hasTransactions(): bool
{
return $this->getAdapter()->hasTransactions();
}
/**
* @inheritDoc
*/
public function beginTransaction(): void
{
$this->getAdapter()->beginTransaction();
}
/**
* @inheritDoc
*/
public function commitTransaction(): void
{
$this->getAdapter()->commitTransaction();
}
/**
* @inheritDoc
*/
public function rollbackTransaction(): void
{
$this->getAdapter()->rollbackTransaction();
}
/**
* @inheritDoc
*/
public function quoteTableName(string $tableName): string
{
return $this->getAdapter()->quoteTableName($tableName);
}
/**
* @inheritDoc
*/
public function quoteColumnName(string $columnName): string
{
return $this->getAdapter()->quoteColumnName($columnName);
}
/**
* @inheritDoc
*/
public function hasTable(string $tableName): bool
{
return $this->getAdapter()->hasTable($tableName);
}
/**
* @inheritDoc
*/
public function createTable(Table $table, array $columns = [], array $indexes = []): void
{
$this->getAdapter()->createTable($table, $columns, $indexes);
}
/**
* @inheritDoc
*/
public function getColumns(string $tableName): array
{
return $this->getAdapter()->getColumns($tableName);
}
/**
* @inheritDoc
*/
public function hasColumn(string $tableName, string $columnName): bool
{
return $this->getAdapter()->hasColumn($tableName, $columnName);
}
/**
* @inheritDoc
*/
public function hasIndex(string $tableName, $columns): bool
{
return $this->getAdapter()->hasIndex($tableName, $columns);
}
/**
* @inheritDoc
*/
public function hasIndexByName(string $tableName, string $indexName): bool
{
return $this->getAdapter()->hasIndexByName($tableName, $indexName);
}
/**
* @inheritDoc
*/
public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool
{
return $this->getAdapter()->hasPrimaryKey($tableName, $columns, $constraint);
}
/**
* @inheritDoc
*/
public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool
{
return $this->getAdapter()->hasForeignKey($tableName, $columns, $constraint);
}
/**
* @inheritDoc
*/
public function getSqlType($type, ?int $limit = null): array
{
return $this->getAdapter()->getSqlType($type, $limit);
}
/**
* @inheritDoc
*/
public function createDatabase(string $name, array $options = []): void
{
$this->getAdapter()->createDatabase($name, $options);
}
/**
* @inheritDoc
*/
public function hasDatabase(string $name): bool
{
return $this->getAdapter()->hasDatabase($name);
}
/**
* @inheritDoc
*/
public function dropDatabase(string $name): void
{
$this->getAdapter()->dropDatabase($name);
}
/**
* @inheritDoc
*/
public function createSchema(string $schemaName = 'public'): void
{
$this->getAdapter()->createSchema($schemaName);
}
/**
* @inheritDoc
*/
public function dropSchema(string $schemaName): void
{
$this->getAdapter()->dropSchema($schemaName);
}
/**
* @inheritDoc
*/
public function truncateTable(string $tableName): void
{
$this->getAdapter()->truncateTable($tableName);
}
/**
* @inheritDoc
*/
public function castToBool($value)
{
return $this->getAdapter()->castToBool($value);
}
/**
* @return \PDO
*/
public function getConnection()
{
return $this->getAdapter()->getConnection();
}
/**
* @inheritDoc
*/
public function executeActions(Table $table, array $actions): void
{
$this->getAdapter()->executeActions($table, $actions);
}
/**
* @inheritDoc
*/
public function getQueryBuilder(): Query
{
return $this->getAdapter()->getQueryBuilder();
}
}

View File

@@ -0,0 +1,139 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;
use Phinx\Db\Table\Table;
/**
* Represents an adapter that is capable of directly executing alter
* instructions, without having to plan them first.
*/
interface DirectActionInterface
{
/**
* Renames the specified database table.
*
* @param string $tableName Table name
* @param string $newName New Name
* @return void
*/
public function renameTable(string $tableName, string $newName): void;
/**
* Drops the specified database table.
*
* @param string $tableName Table name
* @return void
*/
public function dropTable(string $tableName): void;
/**
* Changes the primary key of the specified database table.
*
* @param \Phinx\Db\Table\Table $table Table
* @param string|string[]|null $newColumns Column name(s) to belong to the primary key, or null to drop the key
* @return void
*/
public function changePrimaryKey(Table $table, $newColumns): void;
/**
* Changes the comment of the specified database table.
*
* @param \Phinx\Db\Table\Table $table Table
* @param string|null $newComment New comment string, or null to drop the comment
* @return void
*/
public function changeComment(Table $table, ?string $newComment): void;
/**
* Adds the specified column to a database table.
*
* @param \Phinx\Db\Table\Table $table Table
* @param \Phinx\Db\Table\Column $column Column
* @return void
*/
public function addColumn(Table $table, Column $column): void;
/**
* 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(string $tableName, string $columnName, string $newColumnName): void;
/**
* Change a table column type.
*
* @param string $tableName Table name
* @param string $columnName Column Name
* @param \Phinx\Db\Table\Column $newColumn New Column
* @return void
*/
public function changeColumn(string $tableName, string $columnName, Column $newColumn): void;
/**
* Drops the specified column.
*
* @param string $tableName Table name
* @param string $columnName Column Name
* @return void
*/
public function dropColumn(string $tableName, string $columnName): void;
/**
* Adds the specified index to a database table.
*
* @param \Phinx\Db\Table\Table $table Table
* @param \Phinx\Db\Table\Index $index Index
* @return void
*/
public function addIndex(Table $table, Index $index): void;
/**
* Drops the specified index from a database table.
*
* @param string $tableName the name of the table
* @param string|string[] $columns Column(s)
* @return void
*/
public function dropIndex(string $tableName, $columns): void;
/**
* Drops the index specified by name from a database table.
*
* @param string $tableName The table name where the index is
* @param string $indexName The name of the index
* @return void
*/
public function dropIndexByName(string $tableName, string $indexName): void;
/**
* Adds the specified foreign key to a database table.
*
* @param \Phinx\Db\Table\Table $table The table to add the foreign key to
* @param \Phinx\Db\Table\ForeignKey $foreignKey The foreign key to add
* @return void
*/
public function addForeignKey(Table $table, ForeignKey $foreignKey): void;
/**
* Drops the specified foreign key from a database table.
*
* @param string $tableName The table to drop the foreign key from
* @param string[] $columns Column(s)
* @param string|null $constraint Constraint name
* @return void
*/
public function dropForeignKey(string $tableName, array $columns, ?string $constraint = null): void;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,129 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
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;
/**
* Phinx Proxy Adapter.
*
* Used for recording migration commands to automatically reverse them.
*
* @author Rob Morgan <robbym@gmail.com>
*/
class ProxyAdapter extends AdapterWrapper
{
/**
* @var \Phinx\Db\Action\Action[]
*/
protected $commands = [];
/**
* @inheritDoc
*/
public function getAdapterType(): string
{
return 'ProxyAdapter';
}
/**
* @inheritDoc
*/
public function createTable(Table $table, array $columns = [], array $indexes = []): void
{
$this->commands[] = new CreateTable($table);
}
/**
* @inheritDoc
*/
public function executeActions(Table $table, array $actions): void
{
$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 \Phinx\Db\Plan\Intent
*/
public function getInvertedCommands(): Intent
{
$inverted = new Intent();
foreach (array_reverse($this->commands) as $command) {
switch (true) {
case $command instanceof CreateTable:
/** @var \Phinx\Db\Action\CreateTable $command */
$inverted->addAction(new DropTable($command->getTable()));
break;
case $command instanceof RenameTable:
/** @var \Phinx\Db\Action\RenameTable $command */
$inverted->addAction(new RenameTable(new Table($command->getNewName()), $command->getTable()->getName()));
break;
case $command instanceof AddColumn:
/** @var \Phinx\Db\Action\AddColumn $command */
$inverted->addAction(new RemoveColumn($command->getTable(), $command->getColumn()));
break;
case $command instanceof RenameColumn:
/** @var \Phinx\Db\Action\RenameColumn $command */
$column = clone $command->getColumn();
$name = $column->getName();
$column->setName($command->getNewName());
$inverted->addAction(new RenameColumn($command->getTable(), $column, $name));
break;
case $command instanceof AddIndex:
/** @var \Phinx\Db\Action\AddIndex $command */
$inverted->addAction(new DropIndex($command->getTable(), $command->getIndex()));
break;
case $command instanceof AddForeignKey:
/** @var \Phinx\Db\Action\AddForeignKey $command */
$inverted->addAction(new DropForeignKey($command->getTable(), $command->getForeignKey()));
break;
default:
throw new IrreversibleMigrationException(sprintf(
'Cannot reverse a "%s" command',
get_class($command)
));
}
}
return $inverted;
}
/**
* Execute the recorded commands in reverse.
*
* @return void
*/
public function executeInvertedCommands(): void
{
$plan = new Plan($this->getInvertedCommands());
$plan->executeInverse($this->getAdapter());
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,494 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use BadMethodCallException;
use InvalidArgumentException;
use Phinx\Db\Action\AddColumn;
use Phinx\Db\Action\AddForeignKey;
use Phinx\Db\Action\AddIndex;
use Phinx\Db\Action\ChangeColumn;
use Phinx\Db\Action\ChangeComment;
use Phinx\Db\Action\ChangePrimaryKey;
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.
*
* Used for inserting a prefix or suffix into table names.
*
* @author Samuel Fisher <sam@sfisher.co>
*/
class TablePrefixAdapter extends AdapterWrapper implements DirectActionInterface
{
/**
* @inheritDoc
*/
public function getAdapterType(): string
{
return 'TablePrefixAdapter';
}
/**
* @inheritDoc
*/
public function hasTable(string $tableName): bool
{
$adapterTableName = $this->getAdapterTableName($tableName);
return parent::hasTable($adapterTableName);
}
/**
* @inheritDoc
*/
public function createTable(Table $table, array $columns = [], array $indexes = []): void
{
$adapterTable = new Table(
$this->getAdapterTableName($table->getName()),
$table->getOptions()
);
parent::createTable($adapterTable, $columns, $indexes);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function changePrimaryKey(Table $table, $newColumns): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTable = new Table(
$this->getAdapterTableName($table->getName()),
$table->getOptions()
);
$adapter->changePrimaryKey($adapterTable, $newColumns);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function changeComment(Table $table, ?string $newComment): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTable = new Table(
$this->getAdapterTableName($table->getName()),
$table->getOptions()
);
$adapter->changeComment($adapterTable, $newComment);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function renameTable(string $tableName, string $newTableName): void
{
$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);
$adapter->renameTable($adapterTableName, $adapterNewTableName);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropTable(string $tableName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($tableName);
$adapter->dropTable($adapterTableName);
}
/**
* @inheritDoc
*/
public function truncateTable(string $tableName): void
{
$adapterTableName = $this->getAdapterTableName($tableName);
parent::truncateTable($adapterTableName);
}
/**
* @inheritDoc
*/
public function getColumns(string $tableName): array
{
$adapterTableName = $this->getAdapterTableName($tableName);
return parent::getColumns($adapterTableName);
}
/**
* @inheritDoc
*/
public function hasColumn(string $tableName, string $columnName): bool
{
$adapterTableName = $this->getAdapterTableName($tableName);
return parent::hasColumn($adapterTableName, $columnName);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function addColumn(Table $table, Column $column): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($table->getName());
$adapterTable = new Table($adapterTableName, $table->getOptions());
$adapter->addColumn($adapterTable, $column);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function renameColumn(string $tableName, string $columnName, string $newColumnName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($tableName);
$adapter->renameColumn($adapterTableName, $columnName, $newColumnName);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function changeColumn(string $tableName, string $columnName, Column $newColumn): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($tableName);
$adapter->changeColumn($adapterTableName, $columnName, $newColumn);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropColumn(string $tableName, string $columnName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($tableName);
$adapter->dropColumn($adapterTableName, $columnName);
}
/**
* @inheritDoc
*/
public function hasIndex(string $tableName, $columns): bool
{
$adapterTableName = $this->getAdapterTableName($tableName);
return parent::hasIndex($adapterTableName, $columns);
}
/**
* @inheritDoc
*/
public function hasIndexByName(string $tableName, string $indexName): bool
{
$adapterTableName = $this->getAdapterTableName($tableName);
return parent::hasIndexByName($adapterTableName, $indexName);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function addIndex(Table $table, Index $index): void
{
$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);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropIndex(string $tableName, $columns): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($tableName);
$adapter->dropIndex($adapterTableName, $columns);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropIndexByName(string $tableName, string $indexName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($tableName);
$adapter->dropIndexByName($adapterTableName, $indexName);
}
/**
* @inheritDoc
*/
public function hasPrimaryKey(string $tableName, $columns, ?string $constraint = null): bool
{
$adapterTableName = $this->getAdapterTableName($tableName);
return parent::hasPrimaryKey($adapterTableName, $columns, $constraint);
}
/**
* @inheritDoc
*/
public function hasForeignKey(string $tableName, $columns, ?string $constraint = null): bool
{
$adapterTableName = $this->getAdapterTableName($tableName);
return parent::hasForeignKey($adapterTableName, $columns, $constraint);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function addForeignKey(Table $table, ForeignKey $foreignKey): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($table->getName());
$adapterTable = new Table($adapterTableName, $table->getOptions());
$adapter->addForeignKey($adapterTable, $foreignKey);
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropForeignKey(string $tableName, array $columns, ?string $constraint = null): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The underlying adapter does not implement DirectActionInterface');
}
$adapterTableName = $this->getAdapterTableName($tableName);
$adapter->dropForeignKey($adapterTableName, $columns, $constraint);
}
/**
* @inheritDoc
*/
public function insert(Table $table, array $row): void
{
$adapterTableName = $this->getAdapterTableName($table->getName());
$adapterTable = new Table($adapterTableName, $table->getOptions());
parent::insert($adapterTable, $row);
}
/**
* @inheritDoc
*/
public function bulkinsert(Table $table, array $rows): void
{
$adapterTableName = $this->getAdapterTableName($table->getName());
$adapterTable = new Table($adapterTableName, $table->getOptions());
parent::bulkinsert($adapterTable, $rows);
}
/**
* Gets the table prefix.
*
* @return string
*/
public function getPrefix(): string
{
return (string)$this->getOption('table_prefix');
}
/**
* Gets the table suffix.
*
* @return string
*/
public function getSuffix(): string
{
return (string)$this->getOption('table_suffix');
}
/**
* Applies the prefix and suffix to the table name.
*
* @param string $tableName Table name
* @return string
*/
public function getAdapterTableName(string $tableName): string
{
return $this->getPrefix() . $tableName . $this->getSuffix();
}
/**
* {@inheritDoc}
*
* @throws \InvalidArgumentException
* @return void
*/
public function executeActions(Table $table, array $actions): void
{
$adapterTableName = $this->getAdapterTableName($table->getName());
$adapterTable = new Table($adapterTableName, $table->getOptions());
foreach ($actions as $k => $action) {
switch (true) {
case $action instanceof AddColumn:
/** @var \Phinx\Db\Action\AddColumn $action */
$actions[$k] = new AddColumn($adapterTable, $action->getColumn());
break;
case $action instanceof AddIndex:
/** @var \Phinx\Db\Action\AddIndex $action */
$actions[$k] = new AddIndex($adapterTable, $action->getIndex());
break;
case $action instanceof AddForeignKey:
/** @var \Phinx\Db\Action\AddForeignKey $action */
$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:
/** @var \Phinx\Db\Action\ChangeColumn $action */
$actions[$k] = new ChangeColumn($adapterTable, $action->getColumnName(), $action->getColumn());
break;
case $action instanceof DropForeignKey:
/** @var \Phinx\Db\Action\DropForeignKey $action */
$actions[$k] = new DropForeignKey($adapterTable, $action->getForeignKey());
break;
case $action instanceof DropIndex:
/** @var \Phinx\Db\Action\DropIndex $action */
$actions[$k] = new DropIndex($adapterTable, $action->getIndex());
break;
case $action instanceof DropTable:
/** @var \Phinx\Db\Action\DropTable $action */
$actions[$k] = new DropTable($adapterTable);
break;
case $action instanceof RemoveColumn:
/** @var \Phinx\Db\Action\RemoveColumn $action */
$actions[$k] = new RemoveColumn($adapterTable, $action->getColumn());
break;
case $action instanceof RenameColumn:
/** @var \Phinx\Db\Action\RenameColumn $action */
$actions[$k] = new RenameColumn($adapterTable, $action->getColumn(), $action->getNewName());
break;
case $action instanceof RenameTable:
/** @var \Phinx\Db\Action\RenameTable $action */
$actions[$k] = new RenameTable($adapterTable, $this->getAdapterTableName($action->getNewName()));
break;
case $action instanceof ChangePrimaryKey:
/** @var \Phinx\Db\Action\ChangePrimaryKey $action */
$actions[$k] = new ChangePrimaryKey($adapterTable, $action->getNewColumns());
break;
case $action instanceof ChangeComment:
/** @var \Phinx\Db\Action\ChangeComment $action */
$actions[$k] = new ChangeComment($adapterTable, $action->getNewComment());
break;
default:
throw new InvalidArgumentException(
sprintf("Forgot to implement table prefixing for action: '%s'", get_class($action))
);
}
}
parent::executeActions($adapterTable, $actions);
}
}

View File

@@ -0,0 +1,423 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use BadMethodCallException;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\ForeignKey;
use Phinx\Db\Table\Index;
use Phinx\Db\Table\Table;
use think\console\Output as OutputInterface;
/**
* Wraps any adapter to record the time spend executing its commands
*/
class TimedOutputAdapter extends AdapterWrapper implements DirectActionInterface
{
/**
* @inheritDoc
*/
public function getAdapterType(): string
{
return $this->getAdapter()->getAdapterType();
}
/**
* Start timing a command.
*
* @return callable A function that is to be called when the command finishes
*/
public function startCommandTimer(): callable
{
$started = microtime(true);
return function () use ($started) {
$end = microtime(true);
if (OutputInterface::VERBOSITY_VERBOSE <= $this->getOutput()->getVerbosity()) {
$this->getOutput()->writeln(' -> ' . sprintf('%.4fs', $end - $started));
}
};
}
/**
* Write a Phinx command to the output.
*
* @param string $command Command Name
* @param array $args Command Args
* @return void
*/
public function writeCommand(string $command, array $args = []): void
{
if (OutputInterface::VERBOSITY_VERBOSE > $this->getOutput()->getVerbosity()) {
return;
}
if (count($args)) {
$outArr = [];
foreach ($args as $arg) {
if (is_array($arg)) {
$arg = array_map(
function ($value) {
return '\'' . $value . '\'';
},
$arg
);
$outArr[] = '[' . implode(', ', $arg) . ']';
continue;
}
$outArr[] = '\'' . $arg . '\'';
}
$this->getOutput()->writeln(' -- ' . $command . '(' . implode(', ', $outArr) . ')');
return;
}
$this->getOutput()->writeln(' -- ' . $command);
}
/**
* @inheritDoc
*/
public function insert(Table $table, array $row): void
{
$end = $this->startCommandTimer();
$this->writeCommand('insert', [$table->getName()]);
parent::insert($table, $row);
$end();
}
/**
* @inheritDoc
*/
public function bulkinsert(Table $table, array $rows): void
{
$end = $this->startCommandTimer();
$this->writeCommand('bulkinsert', [$table->getName()]);
parent::bulkinsert($table, $rows);
$end();
}
/**
* @inheritDoc
*/
public function createTable(Table $table, array $columns = [], array $indexes = []): void
{
$end = $this->startCommandTimer();
$this->writeCommand('createTable', [$table->getName()]);
parent::createTable($table, $columns, $indexes);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function changePrimaryKey(Table $table, $newColumns): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('changePrimaryKey', [$table->getName()]);
$adapter->changePrimaryKey($table, $newColumns);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function changeComment(Table $table, ?string $newComment): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('changeComment', [$table->getName()]);
$adapter->changeComment($table, $newComment);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function renameTable(string $tableName, string $newTableName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('renameTable', [$tableName, $newTableName]);
$adapter->renameTable($tableName, $newTableName);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropTable(string $tableName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('dropTable', [$tableName]);
$adapter->dropTable($tableName);
$end();
}
/**
* @inheritDoc
*/
public function truncateTable(string $tableName): void
{
$end = $this->startCommandTimer();
$this->writeCommand('truncateTable', [$tableName]);
parent::truncateTable($tableName);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function addColumn(Table $table, Column $column): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand(
'addColumn',
[
$table->getName(),
$column->getName(),
$column->getType(),
]
);
$adapter->addColumn($table, $column);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function renameColumn(string $tableName, string $columnName, string $newColumnName): void
{
$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]);
$adapter->renameColumn($tableName, $columnName, $newColumnName);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function changeColumn(string $tableName, string $columnName, Column $newColumn): void
{
$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()]);
$adapter->changeColumn($tableName, $columnName, $newColumn);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropColumn(string $tableName, string $columnName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('dropColumn', [$tableName, $columnName]);
$adapter->dropColumn($tableName, $columnName);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function addIndex(Table $table, Index $index): void
{
$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()]);
$adapter->addIndex($table, $index);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropIndex(string $tableName, $columns): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('dropIndex', [$tableName, $columns]);
$adapter->dropIndex($tableName, $columns);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropIndexByName(string $tableName, string $indexName): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('dropIndexByName', [$tableName, $indexName]);
$adapter->dropIndexByName($tableName, $indexName);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function addForeignKey(Table $table, ForeignKey $foreignKey): void
{
$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()]);
$adapter->addForeignKey($table, $foreignKey);
$end();
}
/**
* {@inheritDoc}
*
* @throws \BadMethodCallException
* @return void
*/
public function dropForeignKey(string $tableName, array $columns, ?string $constraint = null): void
{
$adapter = $this->getAdapter();
if (!$adapter instanceof DirectActionInterface) {
throw new BadMethodCallException('The adapter needs to implement DirectActionInterface');
}
$end = $this->startCommandTimer();
$this->writeCommand('dropForeignKey', [$tableName, $columns]);
$adapter->dropForeignKey($tableName, $columns, $constraint);
$end();
}
/**
* @inheritDoc
*/
public function createDatabase(string $name, array $options = []): void
{
$end = $this->startCommandTimer();
$this->writeCommand('createDatabase', [$name]);
parent::createDatabase($name, $options);
$end();
}
/**
* @inheritDoc
*/
public function dropDatabase(string $name): void
{
$end = $this->startCommandTimer();
$this->writeCommand('dropDatabase', [$name]);
parent::dropDatabase($name);
$end();
}
/**
* @inheritDoc
*/
public function createSchema(string $name = 'public'): void
{
$end = $this->startCommandTimer();
$this->writeCommand('createSchema', [$name]);
parent::createSchema($name);
$end();
}
/**
* @inheritDoc
*/
public function dropSchema(string $name): void
{
$end = $this->startCommandTimer();
$this->writeCommand('dropSchema', [$name]);
parent::dropSchema($name);
$end();
}
/**
* @inheritDoc
*/
public function executeActions(Table $table, array $actions): void
{
$end = $this->startCommandTimer();
$this->writeCommand(sprintf('Altering table %s', $table->getName()));
parent::executeActions($table, $actions);
$end();
}
}

View File

@@ -0,0 +1,19 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
use RuntimeException;
/**
* Exception thrown when a column type doesn't match a Phinx type.
*
* @author Martijn Gastkemper <martijngastkemper@gmail.com>
*/
class UnsupportedColumnTypeException extends RuntimeException
{
}

View File

@@ -0,0 +1,39 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Adapter;
/**
* Wrapper Interface.
*
* @author Woody Gilk <woody.gilk@gmail.com>
*/
interface WrapperInterface
{
/**
* Class constructor, must always wrap another adapter.
*
* @param \Phinx\Db\Adapter\AdapterInterface $adapter Adapter
*/
public function __construct(AdapterInterface $adapter);
/**
* Sets the database adapter to proxy commands to.
*
* @param \Phinx\Db\Adapter\AdapterInterface $adapter Adapter
* @return \Phinx\Db\Adapter\AdapterInterface
*/
public function setAdapter(AdapterInterface $adapter): AdapterInterface;
/**
* Gets the database adapter.
*
* @throws \RuntimeException if the adapter has not been set
* @return \Phinx\Db\Adapter\AdapterInterface
*/
public function getAdapter(): AdapterInterface;
}

View File

@@ -0,0 +1,72 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Plan;
use Phinx\Db\Action\Action;
use Phinx\Db\Table\Table;
/**
* A collection of ALTER actions for a single table
*/
class AlterTable
{
/**
* The table
*
* @var \Phinx\Db\Table\Table
*/
protected $table;
/**
* The list of actions to execute
*
* @var \Phinx\Db\Action\Action[]
*/
protected $actions = [];
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to change
*/
public function __construct(Table $table)
{
$this->table = $table;
}
/**
* Adds another action to the collection
*
* @param \Phinx\Db\Action\Action $action The action to add
* @return void
*/
public function addAction(Action $action): void
{
$this->actions[] = $action;
}
/**
* Returns the table associated to this collection
*
* @return \Phinx\Db\Table\Table
*/
public function getTable(): Table
{
return $this->table;
}
/**
* Returns an array with all collected actions
*
* @return \Phinx\Db\Action\Action[]
*/
public function getActions(): array
{
return $this->actions;
}
}

View File

@@ -0,0 +1,55 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Plan;
use Phinx\Db\Action\Action;
/**
* An intent is a collection of actions for many tables
*/
class Intent
{
/**
* List of actions to be executed
*
* @var \Phinx\Db\Action\Action[]
*/
protected $actions = [];
/**
* Adds a new action to the collection
*
* @param \Phinx\Db\Action\Action $action The action to add
* @return void
*/
public function addAction(Action $action): void
{
$this->actions[] = $action;
}
/**
* Returns the full list of actions
*
* @return \Phinx\Db\Action\Action[]
*/
public function getActions(): array
{
return $this->actions;
}
/**
* Merges another Intent object with this one
*
* @param \Phinx\Db\Plan\Intent $another The other intent to merge in
* @return void
*/
public function merge(Intent $another): void
{
$this->actions = array_merge($this->actions, $another->getActions());
}
}

View File

@@ -0,0 +1,101 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Plan;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Index;
use Phinx\Db\Table\Table;
/**
* Represents the collection of actions for creating a new table
*/
class NewTable
{
/**
* The table to create
*
* @var \Phinx\Db\Table\Table
*/
protected $table;
/**
* The list of columns to add
*
* @var \Phinx\Db\Table\Column[]
*/
protected $columns = [];
/**
* The list of indexes to create
*
* @var \Phinx\Db\Table\Index[]
*/
protected $indexes = [];
/**
* Constructor
*
* @param \Phinx\Db\Table\Table $table The table to create
*/
public function __construct(Table $table)
{
$this->table = $table;
}
/**
* Adds a column to the collection
*
* @param \Phinx\Db\Table\Column $column The column description
* @return void
*/
public function addColumn(Column $column): void
{
$this->columns[] = $column;
}
/**
* Adds an index to the collection
*
* @param \Phinx\Db\Table\Index $index The index description
* @return void
*/
public function addIndex(Index $index): void
{
$this->indexes[] = $index;
}
/**
* Returns the table object associated to this collection
*
* @return \Phinx\Db\Table\Table
*/
public function getTable(): Table
{
return $this->table;
}
/**
* Returns the columns collection
*
* @return \Phinx\Db\Table\Column[]
*/
public function getColumns(): array
{
return $this->columns;
}
/**
* Returns the indexes collection
*
* @return \Phinx\Db\Table\Index[]
*/
public function getIndexes(): array
{
return $this->indexes;
}
}

View File

@@ -0,0 +1,492 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Plan;
use ArrayObject;
use Phinx\Db\Action\AddColumn;
use Phinx\Db\Action\AddForeignKey;
use Phinx\Db\Action\AddIndex;
use Phinx\Db\Action\ChangeColumn;
use Phinx\Db\Action\ChangeComment;
use Phinx\Db\Action\ChangePrimaryKey;
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\Adapter\AdapterInterface;
use Phinx\Db\Plan\Solver\ActionSplitter;
use Phinx\Db\Table\Table;
/**
* A Plan takes an Intent and transforms int into a sequence of
* instructions that can be correctly executed by an AdapterInterface.
*
* The main focus of Plan is to arrange the actions in the most efficient
* way possible for the database.
*/
class Plan
{
/**
* List of tables to be created
*
* @var \Phinx\Db\Plan\NewTable[]
*/
protected $tableCreates = [];
/**
* List of table updates
*
* @var \Phinx\Db\Plan\AlterTable[]
*/
protected $tableUpdates = [];
/**
* List of table removals or renames
*
* @var \Phinx\Db\Plan\AlterTable[]
*/
protected $tableMoves = [];
/**
* List of index additions or removals
*
* @var \Phinx\Db\Plan\AlterTable[]
*/
protected $indexes = [];
/**
* List of constraint additions or removals
*
* @var \Phinx\Db\Plan\AlterTable[]
*/
protected $constraints = [];
/**
* List of dropped columns
*
* @var \Phinx\Db\Plan\AlterTable[]
*/
protected $columnRemoves = [];
/**
* Constructor
*
* @param \Phinx\Db\Plan\Intent $intent All the actions that should be executed
*/
public function __construct(Intent $intent)
{
$this->createPlan($intent->getActions());
}
/**
* Parses the given Intent and creates the separate steps to execute
*
* @param \Phinx\Db\Action\Action[] $actions The actions to use for the plan
* @return void
*/
protected function createPlan(array $actions): void
{
$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 \Phinx\Db\Plan\AlterTable[][]
*/
protected function updatesSequence(): array
{
return [
$this->tableUpdates,
$this->constraints,
$this->indexes,
$this->columnRemoves,
$this->tableMoves,
];
}
/**
* Returns a nested list of all the steps to execute in inverse order
*
* @return \Phinx\Db\Plan\AlterTable[][]
*/
protected function inverseUpdatesSequence(): array
{
return [
$this->constraints,
$this->tableMoves,
$this->indexes,
$this->columnRemoves,
$this->tableUpdates,
];
}
/**
* Executes this plan using the given AdapterInterface
*
* @param \Phinx\Db\Adapter\AdapterInterface $executor The executor object for the plan
* @return void
*/
public function execute(AdapterInterface $executor): void
{
foreach ($this->tableCreates as $newTable) {
$executor->createTable($newTable->getTable(), $newTable->getColumns(), $newTable->getIndexes());
}
foreach ($this->updatesSequence() as $updates) {
foreach ($updates as $update) {
$executor->executeActions($update->getTable(), $update->getActions());
}
}
}
/**
* Executes the inverse plan (rollback the actions) with the given AdapterInterface:w
*
* @param \Phinx\Db\Adapter\AdapterInterface $executor The executor object for the plan
* @return void
*/
public function executeInverse(AdapterInterface $executor): void
{
foreach ($this->inverseUpdatesSequence() as $updates) {
foreach ($updates as $update) {
$executor->executeActions($update->getTable(), $update->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(): void
{
foreach ($this->tableMoves as $alterTable) {
foreach ($alterTable->getActions() 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);
$this->columnRemoves = $this->forgetTable($action->getTable(), $this->columnRemoves);
}
}
}
// Renaming a column and then changing the renamed column is something people do,
// but it is a conflicting action. Luckily solving the conflict can be done by moving
// the ChangeColumn action to another AlterTable.
$splitter = new ActionSplitter(
RenameColumn::class,
ChangeColumn::class,
function (RenameColumn $a, ChangeColumn $b) {
return $a->getNewName() === $b->getColumnName();
}
);
$tableUpdates = [];
foreach ($this->tableUpdates as $update) {
$tableUpdates = array_merge($tableUpdates, $splitter($update));
}
$this->tableUpdates = $tableUpdates;
// Dropping indexes used by foreign keys is a conflict, but one we can resolve
// if the foreign key is also scheduled to be dropped. If we can find such a a case,
// we force the execution of the index drop after the foreign key is dropped.
// Changing constraint properties sometimes require dropping it and then
// creating it again with the new stuff. Unfortunately, we have already bundled
// everything together in as few AlterTable statements as we could, so we need to
// resolve this conflict manually.
$splitter = new ActionSplitter(
DropForeignKey::class,
AddForeignKey::class,
function (DropForeignKey $a, AddForeignKey $b) {
return $a->getForeignKey()->getColumns() === $b->getForeignKey()->getColumns();
}
);
$constraints = [];
foreach ($this->constraints as $constraint) {
$constraints = array_merge(
$constraints,
$splitter($this->remapContraintAndIndexConflicts($constraint))
);
}
$this->constraints = $constraints;
}
/**
* Deletes all actions related to the given table and keeps the
* rest
*
* @param \Phinx\Db\Table\Table $table The table to find in the list of actions
* @param \Phinx\Db\Plan\AlterTable[] $actions The actions to transform
* @return \Phinx\Db\Plan\AlterTable[] The list of actions without actions for the given table
*/
protected function forgetTable(Table $table, array $actions): array
{
$result = [];
foreach ($actions as $action) {
if ($action->getTable()->getName() === $table->getName()) {
continue;
}
$result[] = $action;
}
return $result;
}
/**
* Finds all DropForeignKey actions in an AlterTable and moves
* all conflicting DropIndex action in `$this->indexes` into the
* given AlterTable.
*
* @param \Phinx\Db\Plan\AlterTable $alter The collection of actions to inspect
* @return \Phinx\Db\Plan\AlterTable The updated AlterTable object. This function
* has the side effect of changing the `$this->indexes` property.
*/
protected function remapContraintAndIndexConflicts(AlterTable $alter): AlterTable
{
$newAlter = new AlterTable($alter->getTable());
foreach ($alter->getActions() as $action) {
$newAlter->addAction($action);
if ($action instanceof DropForeignKey) {
[$this->indexes, $dropIndexActions] = $this->forgetDropIndex(
$action->getTable(),
$action->getForeignKey()->getColumns(),
$this->indexes
);
foreach ($dropIndexActions as $dropIndexAction) {
$newAlter->addAction($dropIndexAction);
}
}
}
return $newAlter;
}
/**
* Deletes any DropIndex actions for the given table and exact columns
*
* @param \Phinx\Db\Table\Table $table The table to find in the list of actions
* @param string[] $columns The column names to match
* @param \Phinx\Db\Plan\AlterTable[] $actions The actions to transform
* @return array A tuple containing the list of actions without actions for dropping the index
* and a list of drop index actions that were removed.
*/
protected function forgetDropIndex(Table $table, array $columns, array $actions): array
{
$dropIndexActions = new ArrayObject();
$indexes = array_map(function ($alter) use ($table, $columns, $dropIndexActions) {
if ($alter->getTable()->getName() !== $table->getName()) {
return $alter;
}
$newAlter = new AlterTable($table);
foreach ($alter->getActions() as $action) {
if ($action instanceof DropIndex && $action->getIndex()->getColumns() === $columns) {
$dropIndexActions->append($action);
} else {
$newAlter->addAction($action);
}
}
return $newAlter;
}, $actions);
return [$indexes, $dropIndexActions->getArrayCopy()];
}
/**
* Deletes any RemoveColumn actions for the given table and exact columns
*
* @param \Phinx\Db\Table\Table $table The table to find in the list of actions
* @param string[] $columns The column names to match
* @param \Phinx\Db\Plan\AlterTable[] $actions The actions to transform
* @return array A tuple containing the list of actions without actions for removing the column
* and a list of remove column actions that were removed.
*/
protected function forgetRemoveColumn(Table $table, array $columns, array $actions): array
{
$removeColumnActions = new ArrayObject();
$indexes = array_map(function ($alter) use ($table, $columns, $removeColumnActions) {
if ($alter->getTable()->getName() !== $table->getName()) {
return $alter;
}
$newAlter = new AlterTable($table);
foreach ($alter->getActions() as $action) {
if ($action instanceof RemoveColumn && in_array($action->getColumn()->getName(), $columns, true)) {
$removeColumnActions->append($action);
} else {
$newAlter->addAction($action);
}
}
return $newAlter;
}, $actions);
return [$indexes, $removeColumnActions->getArrayCopy()];
}
/**
* Collects all table creation actions from the given intent
*
* @param \Phinx\Db\Action\Action[] $actions The actions to parse
* @return void
*/
protected function gatherCreates(array $actions): void
{
foreach ($actions as $action) {
if ($action instanceof CreateTable) {
$this->tableCreates[$action->getTable()->getName()] = new NewTable($action->getTable());
}
}
foreach ($actions as $action) {
if (
($action instanceof AddColumn || $action instanceof AddIndex)
&& isset($this->tableCreates[$action->getTable()->getName()])
) {
$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(array $actions): void
{
foreach ($actions as $action) {
if (
!($action instanceof AddColumn)
&& !($action instanceof ChangeColumn)
&& !($action instanceof RemoveColumn)
&& !($action instanceof RenameColumn)
) {
continue;
} elseif (isset($this->tableCreates[$action->getTable()->getName()])) {
continue;
}
$table = $action->getTable();
$name = $table->getName();
if ($action instanceof RemoveColumn) {
if (!isset($this->columnRemoves[$name])) {
$this->columnRemoves[$name] = new AlterTable($table);
}
$this->columnRemoves[$name]->addAction($action);
} else {
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(array $actions): void
{
foreach ($actions as $action) {
if (
!($action instanceof DropTable)
&& !($action instanceof RenameTable)
&& !($action instanceof ChangePrimaryKey)
&& !($action instanceof ChangeComment)
) {
continue;
}
$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(array $actions): void
{
foreach ($actions as $action) {
if (!($action instanceof AddIndex) && !($action instanceof DropIndex)) {
continue;
} elseif (isset($this->tableCreates[$action->getTable()->getName()])) {
continue;
}
$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(array $actions): void
{
foreach ($actions as $action) {
if (!($action instanceof AddForeignKey || $action instanceof DropForeignKey)) {
continue;
}
$table = $action->getTable();
$name = $table->getName();
if (!isset($this->constraints[$name])) {
$this->constraints[$name] = new AlterTable($table);
}
$this->constraints[$name]->addAction($action);
}
}
}

View File

@@ -0,0 +1,103 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Plan\Solver;
use Phinx\Db\Plan\AlterTable;
/**
* A Plan takes an Intent and transforms it into a sequence of
* instructions that can be correctly executed by an AdapterInterface.
*
* The main focus of Plan is to arrange the actions in the most efficient
* way possible for the database.
*/
class ActionSplitter
{
/**
* The fully qualified class name of the Action class to match for conflicts
*
* @var string
*/
protected $conflictClass;
/**
* The fully qualified class name of the Action class to match for conflicts, which
* is the dual of $conflictClass. For example `AddColumn` and `DropColumn` are duals.
*
* @var string
*/
protected $conflictClassDual;
/**
* A callback used to signal the actual presence of a conflict, that will be used to
* partition the AlterTable into non-conflicting parts.
*
* The callback receives as first argument amn instance of $conflictClass and as second
* argument an instance of $conflictClassDual
*
* @var callable
*/
protected $conflictFilter;
/**
* Comstructor
*
* @param string $conflictClass The fully qualified class name of the Action class to match for conflicts
* @param string $conflictClassDual The fully qualified class name of the Action class to match for conflicts,
* which is the dual of $conflictClass. For example `AddColumn` and `DropColumn` are duals.
* @param callable $conflictFilter The collection of actions to inspect
*/
public function __construct(string $conflictClass, string $conflictClassDual, callable $conflictFilter)
{
$this->conflictClass = $conflictClass;
$this->conflictClassDual = $conflictClassDual;
$this->conflictFilter = $conflictFilter;
}
/**
* Returs a sequence of AlterTable instructions that are non conflicting
* based on the constructor parameters.
*
* @param \Phinx\Db\Plan\AlterTable $alter The collection of actions to inspect
* @return \Phinx\Db\Plan\AlterTable[] A list of AlterTable that can be executed without
* this type of conflict
*/
public function __invoke(AlterTable $alter): array
{
$conflictActions = array_filter($alter->getActions(), function ($action) {
return $action instanceof $this->conflictClass;
});
$originalAlter = new AlterTable($alter->getTable());
$newAlter = new AlterTable($alter->getTable());
foreach ($alter->getActions() as $action) {
if (!$action instanceof $this->conflictClassDual) {
$originalAlter->addAction($action);
continue;
}
$found = false;
$matches = $this->conflictFilter;
foreach ($conflictActions as $ca) {
if ($matches($ca, $action)) {
$found = true;
break;
}
}
if ($found) {
$newAlter->addAction($action);
} else {
$originalAlter->addAction($action);
}
}
return [$originalAlter, $newAlter];
}
}

721
extend/phinx/Db/Table.php Normal file
View File

@@ -0,0 +1,721 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db;
use InvalidArgumentException;
use Phinx\Db\Action\AddColumn;
use Phinx\Db\Action\AddForeignKey;
use Phinx\Db\Action\AddIndex;
use Phinx\Db\Action\ChangeColumn;
use Phinx\Db\Action\ChangeComment;
use Phinx\Db\Action\ChangePrimaryKey;
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\Adapter\AdapterInterface;
use Phinx\Db\Plan\Intent;
use Phinx\Db\Plan\Plan;
use Phinx\Db\Table\Column;
use Phinx\Db\Table\Table as TableValue;
use Phinx\Util\Literal;
use RuntimeException;
/**
* This object is based loosely on: https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
*/
class Table
{
/**
* @var \Phinx\Db\Table\Table
*/
protected $table;
/**
* @var \Phinx\Db\Adapter\AdapterInterface|null
*/
protected $adapter;
/**
* @var \Phinx\Db\Plan\Intent
*/
protected $actions;
/**
* @var array
*/
protected $data = [];
/**
* @param string $name Table Name
* @param array<string, mixed> $options Options
* @param \Phinx\Db\Adapter\AdapterInterface|null $adapter Database Adapter
*/
public function __construct(string $name, array $options = [], ?AdapterInterface $adapter = null)
{
$this->table = new TableValue($name, $options);
$this->actions = new Intent();
if ($adapter !== null) {
$this->setAdapter($adapter);
}
}
/**
* Gets the table name.
*
* @return string
*/
public function getName(): string
{
return $this->table->getName();
}
/**
* Gets the table options.
*
* @return array<string, mixed>
*/
public function getOptions(): array
{
return $this->table->getOptions();
}
/**
* Gets the table name and options as an object
*
* @return \Phinx\Db\Table\Table
*/
public function getTable(): TableValue
{
return $this->table;
}
/**
* Sets the database adapter.
*
* @param \Phinx\Db\Adapter\AdapterInterface $adapter Database Adapter
* @return $this
*/
public function setAdapter(AdapterInterface $adapter)
{
$this->adapter = $adapter;
return $this;
}
/**
* Gets the database adapter.
*
* @throws \RuntimeException
* @return \Phinx\Db\Adapter\AdapterInterface
*/
public function getAdapter(): AdapterInterface
{
if (!$this->adapter) {
throw new RuntimeException('There is no database adapter set yet, cannot proceed');
}
return $this->adapter;
}
/**
* Does the table have pending actions?
*
* @return bool
*/
public function hasPendingActions(): bool
{
return count($this->actions->getActions()) > 0 || count($this->data) > 0;
}
/**
* Does the table exist?
*
* @return bool
*/
public function exists(): bool
{
return $this->getAdapter()->hasTable($this->getName());
}
/**
* Drops the database table.
*
* @return $this
*/
public function drop()
{
$this->actions->addAction(new DropTable($this->table));
return $this;
}
/**
* Renames the database table.
*
* @param string $newTableName New Table Name
* @return $this
*/
public function rename(string $newTableName)
{
$this->actions->addAction(new RenameTable($this->table, $newTableName));
return $this;
}
/**
* Changes the primary key of the database table.
*
* @param string|string[]|null $columns Column name(s) to belong to the primary key, or null to drop the key
* @return $this
*/
public function changePrimaryKey($columns)
{
$this->actions->addAction(new ChangePrimaryKey($this->table, $columns));
return $this;
}
/**
* Checks to see if a primary key exists.
*
* @param string|string[] $columns Column(s)
* @param string|null $constraint Constraint names
* @return bool
*/
public function hasPrimaryKey($columns, ?string $constraint = null): bool
{
return $this->getAdapter()->hasPrimaryKey($this->getName(), $columns, $constraint);
}
/**
* Changes the comment of the database table.
*
* @param string|null $comment New comment string, or null to drop the comment
* @return $this
*/
public function changeComment(?string $comment)
{
$this->actions->addAction(new ChangeComment($this->table, $comment));
return $this;
}
/**
* Gets an array of the table columns.
*
* @return \Phinx\Db\Table\Column[]
*/
public function getColumns(): array
{
return $this->getAdapter()->getColumns($this->getName());
}
/**
* Gets a table column if it exists.
*
* @param string $name Column name
* @return \Phinx\Db\Table\Column|null
*/
public function getColumn(string $name): ?Column
{
$columns = array_filter(
$this->getColumns(),
function ($column) use ($name) {
return $column->getName() === $name;
}
);
return array_pop($columns);
}
/**
* Sets an array of data to be inserted.
*
* @param array $data Data
* @return $this
*/
public function setData(array $data)
{
$this->data = $data;
return $this;
}
/**
* Gets the data waiting to be inserted.
*
* @return array
*/
public function getData(): array
{
return $this->data;
}
/**
* Resets all of the pending data to be inserted
*
* @return void
*/
public function resetData(): void
{
$this->setData([]);
}
/**
* Resets all of the pending table changes.
*
* @return void
*/
public function reset(): void
{
$this->actions = new Intent();
$this->resetData();
}
/**
* Add a table column.
*
* Type can be: string, text, integer, float, decimal, datetime, timestamp,
* time, date, binary, boolean.
*
* Valid options can be: limit, default, null, precision or scale.
*
* @param string|\Phinx\Db\Table\Column $columnName Column Name
* @param string|\Phinx\Util\Literal|null $type Column Type
* @param array<string, mixed> $options Column Options
* @throws \InvalidArgumentException
* @return $this
*/
public function addColumn($columnName, $type = null, array $options = [])
{
if ($columnName instanceof Column) {
$action = new AddColumn($this->table, $columnName);
} elseif ($type instanceof Literal) {
$action = AddColumn::build($this->table, $columnName, $type, $options);
} else {
$action = new AddColumn($this->table, $this->getAdapter()->getColumnForType($columnName, $type, $options));
}
// Delegate to Adapters to check column type
if (!$this->getAdapter()->isValidColumnType($action->getColumn())) {
throw new InvalidArgumentException(sprintf(
'An invalid column type "%s" was specified for column "%s".',
$type,
$action->getColumn()->getName()
));
}
$this->actions->addAction($action);
return $this;
}
/**
* Remove a table column.
*
* @param string $columnName Column Name
* @return $this
*/
public function removeColumn(string $columnName)
{
$action = RemoveColumn::build($this->table, $columnName);
$this->actions->addAction($action);
return $this;
}
/**
* Rename a table column.
*
* @param string $oldName Old Column Name
* @param string $newName New Column Name
* @return $this
*/
public function renameColumn(string $oldName, string $newName)
{
$action = RenameColumn::build($this->table, $oldName, $newName);
$this->actions->addAction($action);
return $this;
}
/**
* Change a table column type.
*
* @param string $columnName Column Name
* @param string|\Phinx\Db\Table\Column|\Phinx\Util\Literal $newColumnType New Column Type
* @param array<string, mixed> $options Options
* @return $this
*/
public function changeColumn(string $columnName, $newColumnType, array $options = [])
{
if ($newColumnType instanceof Column) {
$action = new ChangeColumn($this->table, $columnName, $newColumnType);
} else {
$action = ChangeColumn::build($this->table, $columnName, $newColumnType, $options);
}
$this->actions->addAction($action);
return $this;
}
/**
* Checks to see if a column exists.
*
* @param string $columnName Column Name
* @return bool
*/
public function hasColumn(string $columnName): bool
{
return $this->getAdapter()->hasColumn($this->getName(), $columnName);
}
/**
* Add an index to a database table.
*
* In $options you can specify unique = true/false, and name (index name).
*
* @param string|array|\Phinx\Db\Table\Index $columns Table Column(s)
* @param array<string, mixed> $options Index Options
* @return $this
*/
public function addIndex($columns, array $options = [])
{
$action = AddIndex::build($this->table, $columns, $options);
$this->actions->addAction($action);
return $this;
}
/**
* Removes the given index from a table.
*
* @param string|string[] $columns Columns
* @return $this
*/
public function removeIndex($columns)
{
$action = DropIndex::build($this->table, is_string($columns) ? [$columns] : $columns);
$this->actions->addAction($action);
return $this;
}
/**
* Removes the given index identified by its name from a table.
*
* @param string $name Index name
* @return $this
*/
public function removeIndexByName(string $name)
{
$action = DropIndex::buildFromName($this->table, $name);
$this->actions->addAction($action);
return $this;
}
/**
* Checks to see if an index exists.
*
* @param string|string[] $columns Columns
* @return bool
*/
public function hasIndex($columns): bool
{
return $this->getAdapter()->hasIndex($this->getName(), $columns);
}
/**
* Checks to see if an index specified by name exists.
*
* @param string $indexName Index name
* @return bool
*/
public function hasIndexByName($indexName): bool
{
return $this->getAdapter()->hasIndexByName($this->getName(), $indexName);
}
/**
* Add a foreign key to a database table.
*
* In $options you can specify on_delete|on_delete = cascade|no_action ..,
* on_update, constraint = constraint name.
*
* @param string|string[] $columns Columns
* @param string|\Phinx\Db\Table\Table $referencedTable Referenced Table
* @param string|string[] $referencedColumns Referenced Columns
* @param array<string, mixed> $options Options
* @return $this
*/
public function addForeignKey($columns, $referencedTable, $referencedColumns = ['id'], array $options = [])
{
$action = AddForeignKey::build($this->table, $columns, $referencedTable, $referencedColumns, $options);
$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 constraint name
* @param string|string[] $columns Columns
* @param string|\Phinx\Db\Table\Table $referencedTable Referenced Table
* @param string|string[] $referencedColumns Referenced Columns
* @param array<string, mixed> $options Options
* @return $this
*/
public function addForeignKeyWithName(string $name, $columns, $referencedTable, $referencedColumns = ['id'], array $options = [])
{
$action = AddForeignKey::build(
$this->table,
$columns,
$referencedTable,
$referencedColumns,
$options,
$name
);
$this->actions->addAction($action);
return $this;
}
/**
* Removes the given foreign key from the table.
*
* @param string|string[] $columns Column(s)
* @param string|null $constraint Constraint names
* @return $this
*/
public function dropForeignKey($columns, ?string $constraint = null)
{
$action = DropForeignKey::build($this->table, $columns, $constraint);
$this->actions->addAction($action);
return $this;
}
/**
* Checks to see if a foreign key exists.
*
* @param string|string[] $columns Column(s)
* @param string|null $constraint Constraint names
* @return bool
*/
public function hasForeignKey($columns, ?string $constraint = null): bool
{
return $this->getAdapter()->hasForeignKey($this->getName(), $columns, $constraint);
}
/**
* Add timestamp columns created_at and updated_at to the table.
*
* @param string|false|null $createdAt Alternate name for the created_at column
* @param string|false|null $updatedAt Alternate name for the updated_at column
* @param bool $withTimezone Whether to set the timezone option on the added columns
* @return $this
*/
public function addTimestamps($createdAt = 'created_at', $updatedAt = 'updated_at', bool $withTimezone = false)
{
$createdAt = $createdAt ?? 'created_at';
$updatedAt = $updatedAt ?? 'updated_at';
if (!$createdAt && !$updatedAt) {
throw new \RuntimeException('Cannot set both created_at and updated_at columns to false');
}
if ($createdAt) {
$this->addColumn($createdAt, 'timestamp', [
'null' => false,
'default' => 'CURRENT_TIMESTAMP',
'update' => '',
'timezone' => $withTimezone,
]);
}
if ($updatedAt) {
$this->addColumn($updatedAt, 'timestamp', [
'null' => true,
'default' => null,
'update' => 'CURRENT_TIMESTAMP',
'timezone' => $withTimezone,
]);
}
return $this;
}
/**
* Alias that always sets $withTimezone to true
*
* @see addTimestamps
* @param string|false|null $createdAt Alternate name for the created_at column
* @param string|false|null $updatedAt Alternate name for the updated_at column
* @return $this
*/
public function addTimestampsWithTimezone($createdAt = null, $updatedAt = null)
{
$this->addTimestamps($createdAt, $updatedAt, true);
return $this;
}
/**
* Insert data into the table.
*
* @param array $data array of data in the form:
* array(
* array("col1" => "value1", "col2" => "anotherValue1"),
* array("col2" => "value2", "col2" => "anotherValue2"),
* )
* or array("col1" => "value1", "col2" => "anotherValue1")
* @return $this
*/
public function insert(array $data)
{
// handle array of array situations
$keys = array_keys($data);
$firstKey = array_shift($keys);
if ($firstKey !== null && is_array($data[$firstKey])) {
foreach ($data as $row) {
$this->data[] = $row;
}
return $this;
}
if (count($data) > 0) {
$this->data[] = $data;
}
return $this;
}
/**
* Creates a table from the object instance.
*
* @return void
*/
public function create(): void
{
$this->executeActions(false);
$this->saveData();
$this->reset(); // reset pending changes
}
/**
* Updates a table from the object instance.
*
* @return void
*/
public function update(): void
{
$this->executeActions(true);
$this->saveData();
$this->reset(); // reset pending changes
}
/**
* Commit the pending data waiting for insertion.
*
* @return void
*/
public function saveData(): void
{
$rows = $this->getData();
if (empty($rows)) {
return;
}
$bulk = true;
$row = current($rows);
$c = array_keys($row);
foreach ($this->getData() as $row) {
$k = array_keys($row);
if ($k != $c) {
$bulk = false;
break;
}
}
if ($bulk) {
$this->getAdapter()->bulkinsert($this->table, $this->getData());
} else {
foreach ($this->getData() as $row) {
$this->getAdapter()->insert($this->table, $row);
}
}
$this->resetData();
}
/**
* Immediately truncates the table. This operation cannot be undone
*
* @return void
*/
public function truncate(): void
{
$this->getAdapter()->truncateTable($this->getName());
}
/**
* Commits the table changes.
*
* If the table doesn't exist it is created otherwise it is updated.
*
* @return void
*/
public function save(): void
{
if ($this->exists()) {
$this->update(); // update the table
} else {
$this->create(); // create the table
}
}
/**
* 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(bool $exists): void
{
// 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.
if (!$exists) {
foreach ($this->actions->getActions() as $action) {
if ($action instanceof RenameTable) {
$exists = true;
break;
}
}
}
// 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());
}
}

View File

@@ -0,0 +1,801 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Table;
use Phinx\Config\FeatureFlags;
use Phinx\Db\Adapter\AdapterInterface;
use Phinx\Db\Adapter\PostgresAdapter;
use RuntimeException;
/**
* This object is based loosely on: https://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/Table.html.
*/
class Column
{
public const BIGINTEGER = AdapterInterface::PHINX_TYPE_BIG_INTEGER;
public const SMALLINTEGER = AdapterInterface::PHINX_TYPE_SMALL_INTEGER;
public const TINYINTEGER = AdapterInterface::PHINX_TYPE_TINY_INTEGER;
public const BINARY = AdapterInterface::PHINX_TYPE_BINARY;
public const BOOLEAN = AdapterInterface::PHINX_TYPE_BOOLEAN;
public const CHAR = AdapterInterface::PHINX_TYPE_CHAR;
public const DATE = AdapterInterface::PHINX_TYPE_DATE;
public const DATETIME = AdapterInterface::PHINX_TYPE_DATETIME;
public const DECIMAL = AdapterInterface::PHINX_TYPE_DECIMAL;
public const FLOAT = AdapterInterface::PHINX_TYPE_FLOAT;
public const INTEGER = AdapterInterface::PHINX_TYPE_INTEGER;
public const STRING = AdapterInterface::PHINX_TYPE_STRING;
public const TEXT = AdapterInterface::PHINX_TYPE_TEXT;
public const TIME = AdapterInterface::PHINX_TYPE_TIME;
public const TIMESTAMP = AdapterInterface::PHINX_TYPE_TIMESTAMP;
public const UUID = AdapterInterface::PHINX_TYPE_UUID;
public const BINARYUUID = AdapterInterface::PHINX_TYPE_BINARYUUID;
/** MySQL-only column type */
public const MEDIUMINTEGER = AdapterInterface::PHINX_TYPE_MEDIUM_INTEGER;
/** MySQL-only column type */
public const ENUM = AdapterInterface::PHINX_TYPE_ENUM;
/** MySQL-only column type */
public const SET = AdapterInterface::PHINX_TYPE_STRING;
/** MySQL-only column type */
public const BLOB = AdapterInterface::PHINX_TYPE_BLOB;
/** MySQL-only column type */
public const YEAR = AdapterInterface::PHINX_TYPE_YEAR;
/** MySQL/Postgres-only column type */
public const JSON = AdapterInterface::PHINX_TYPE_JSON;
/** Postgres-only column type */
public const JSONB = AdapterInterface::PHINX_TYPE_JSONB;
/** Postgres-only column type */
public const CIDR = AdapterInterface::PHINX_TYPE_CIDR;
/** Postgres-only column type */
public const INET = AdapterInterface::PHINX_TYPE_INET;
/** Postgres-only column type */
public const MACADDR = AdapterInterface::PHINX_TYPE_MACADDR;
/** Postgres-only column type */
public const INTERVAL = AdapterInterface::PHINX_TYPE_INTERVAL;
/**
* @var string
*/
protected $name;
/**
* @var string|\Phinx\Util\Literal
*/
protected $type;
/**
* @var int|null
*/
protected $limit;
/**
* @var bool
*/
protected $null = true;
/**
* @var mixed
*/
protected $default;
/**
* @var bool
*/
protected $identity = false;
/**
* Postgres-only column option for identity (always|default)
*
* @var ?string
*/
protected $generated = PostgresAdapter::GENERATED_ALWAYS;
/**
* @var int|null
*/
protected $seed;
/**
* @var int|null
*/
protected $increment;
/**
* @var int|null
*/
protected $scale;
/**
* @var string|null
*/
protected $after;
/**
* @var string|null
*/
protected $update;
/**
* @var string|null
*/
protected $comment;
/**
* @var bool
*/
protected $signed = true;
/**
* @var bool
*/
protected $timezone = false;
/**
* @var array
*/
protected $properties = [];
/**
* @var string|null
*/
protected $collation;
/**
* @var string|null
*/
protected $encoding;
/**
* @var int|null
*/
protected $srid;
/**
* @var array|null
*/
protected $values;
/**
* Column constructor
*/
public function __construct()
{
$this->null = FeatureFlags::$columnNullDefault;
}
/**
* Sets the column name.
*
* @param string $name Name
* @return $this
*/
public function setName(string $name)
{
$this->name = $name;
return $this;
}
/**
* Gets the column name.
*
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Sets the column type.
*
* @param string|\Phinx\Util\Literal $type Column type
* @return $this
*/
public function setType($type)
{
$this->type = $type;
return $this;
}
/**
* Gets the column type.
*
* @return string|\Phinx\Util\Literal
*/
public function getType()
{
return $this->type;
}
/**
* Sets the column limit.
*
* @param int|null $limit Limit
* @return $this
*/
public function setLimit(?int $limit)
{
$this->limit = $limit;
return $this;
}
/**
* Gets the column limit.
*
* @return int|null
*/
public function getLimit(): ?int
{
return $this->limit;
}
/**
* Sets whether the column allows nulls.
*
* @param bool $null Null
* @return $this
*/
public function setNull(bool $null)
{
$this->null = (bool)$null;
return $this;
}
/**
* Gets whether the column allows nulls.
*
* @return bool
*/
public function getNull(): bool
{
return $this->null;
}
/**
* Does the column allow nulls?
*
* @return bool
*/
public function isNull(): bool
{
return $this->getNull();
}
/**
* Sets the default column value.
*
* @param mixed $default Default
* @return $this
*/
public function setDefault($default)
{
$this->default = $default;
return $this;
}
/**
* Gets the default column value.
*
* @return mixed
*/
public function getDefault()
{
return $this->default;
}
/**
* Sets generated option for identity columns. Ignored otherwise.
*
* @param string|null $generated Generated option
* @return $this
*/
public function setGenerated(?string $generated)
{
$this->generated = $generated;
return $this;
}
/**
* Gets generated option for identity columns. Null otherwise
*
* @return string|null
*/
public function getGenerated(): ?string
{
return $this->generated;
}
/**
* Sets whether or not the column is an identity column.
*
* @param bool $identity Identity
* @return $this
*/
public function setIdentity(bool $identity)
{
$this->identity = $identity;
return $this;
}
/**
* Gets whether or not the column is an identity column.
*
* @return bool
*/
public function getIdentity(): bool
{
return $this->identity;
}
/**
* Is the column an identity column?
*
* @return bool
*/
public function isIdentity(): bool
{
return $this->getIdentity();
}
/**
* Sets the name of the column to add this column after.
*
* @param string $after After
* @return $this
*/
public function setAfter(string $after)
{
$this->after = $after;
return $this;
}
/**
* Returns the name of the column to add this column after.
*
* @return string|null
*/
public function getAfter(): ?string
{
return $this->after;
}
/**
* Sets the 'ON UPDATE' mysql column function.
*
* @param string $update On Update function
* @return $this
*/
public function setUpdate(string $update)
{
$this->update = $update;
return $this;
}
/**
* Returns the value of the ON UPDATE column function.
*
* @return string|null
*/
public function getUpdate(): ?string
{
return $this->update;
}
/**
* Sets the number precision for decimal or float column.
*
* For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale,
* and the column could store value from -999.99 to 999.99.
*
* @param int|null $precision Number precision
* @return $this
*/
public function setPrecision(?int $precision)
{
$this->setLimit($precision);
return $this;
}
/**
* Gets the number precision for decimal or float column.
*
* For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale,
* and the column could store value from -999.99 to 999.99.
*
* @return int|null
*/
public function getPrecision(): ?int
{
return $this->limit;
}
/**
* Sets the column identity increment.
*
* @param int $increment Number increment
* @return $this
*/
public function setIncrement(int $increment)
{
$this->increment = $increment;
return $this;
}
/**
* Gets the column identity increment.
*
* @return int|null
*/
public function getIncrement(): ?int
{
return $this->increment;
}
/**
* Sets the column identity seed.
*
* @param int $seed Number seed
* @return $this
*/
public function setSeed(int $seed)
{
$this->seed = $seed;
return $this;
}
/**
* Gets the column identity seed.
*
* @return int
*/
public function getSeed(): ?int
{
return $this->seed;
}
/**
* Sets the number scale for decimal or float column.
*
* For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale,
* and the column could store value from -999.99 to 999.99.
*
* @param int|null $scale Number scale
* @return $this
*/
public function setScale(?int $scale)
{
$this->scale = $scale;
return $this;
}
/**
* Gets the number scale for decimal or float column.
*
* For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale,
* and the column could store value from -999.99 to 999.99.
*
* @return int
*/
public function getScale(): ?int
{
return $this->scale;
}
/**
* Sets the number precision and scale for decimal or float column.
*
* For example `DECIMAL(5,2)`, 5 is the precision and 2 is the scale,
* and the column could store value from -999.99 to 999.99.
*
* @param int $precision Number precision
* @param int $scale Number scale
* @return $this
*/
public function setPrecisionAndScale(int $precision, int $scale)
{
$this->setLimit($precision);
$this->scale = $scale;
return $this;
}
/**
* Sets the column comment.
*
* @param string|null $comment Comment
* @return $this
*/
public function setComment(?string $comment)
{
$this->comment = $comment;
return $this;
}
/**
* Gets the column comment.
*
* @return string
*/
public function getComment(): ?string
{
return $this->comment;
}
/**
* Sets whether field should be signed.
*
* @param bool $signed Signed
* @return $this
*/
public function setSigned(bool $signed)
{
$this->signed = (bool)$signed;
return $this;
}
/**
* Gets whether field should be signed.
*
* @return bool
*/
public function getSigned(): bool
{
return $this->signed;
}
/**
* Should the column be signed?
*
* @return bool
*/
public function isSigned(): bool
{
return $this->getSigned();
}
/**
* Sets whether the field should have a timezone identifier.
* Used for date/time columns only!
*
* @param bool $timezone Timezone
* @return $this
*/
public function setTimezone(bool $timezone)
{
$this->timezone = (bool)$timezone;
return $this;
}
/**
* Gets whether field has a timezone identifier.
*
* @return bool
*/
public function getTimezone(): bool
{
return $this->timezone;
}
/**
* Should the column have a timezone?
*
* @return bool
*/
public function isTimezone(): bool
{
return $this->getTimezone();
}
/**
* Sets field properties.
*
* @param array $properties Properties
* @return $this
*/
public function setProperties(array $properties)
{
$this->properties = $properties;
return $this;
}
/**
* Gets field properties
*
* @return array
*/
public function getProperties(): array
{
return $this->properties;
}
/**
* Sets field values.
*
* @param string[]|string $values Value(s)
* @return $this
*/
public function setValues($values)
{
if (!is_array($values)) {
$values = preg_split('/,\s*/', $values) ?: [];
}
$this->values = $values;
return $this;
}
/**
* Gets field values
*
* @return array|null
*/
public function getValues(): ?array
{
return $this->values;
}
/**
* Sets the column collation.
*
* @param string $collation Collation
* @return $this
*/
public function setCollation(string $collation)
{
$this->collation = $collation;
return $this;
}
/**
* Gets the column collation.
*
* @return string|null
*/
public function getCollation(): ?string
{
return $this->collation;
}
/**
* Sets the column character set.
*
* @param string $encoding Encoding
* @return $this
*/
public function setEncoding(string $encoding)
{
$this->encoding = $encoding;
return $this;
}
/**
* Gets the column character set.
*
* @return string|null
*/
public function getEncoding(): ?string
{
return $this->encoding;
}
/**
* Sets the column SRID.
*
* @param int $srid SRID
* @return $this
*/
public function setSrid(int $srid)
{
$this->srid = $srid;
return $this;
}
/**
* Gets the column SRID.
*
* @return int|null
*/
public function getSrid(): ?int
{
return $this->srid;
}
/**
* Gets all allowed options. Each option must have a corresponding `setFoo` method.
*
* @return array
*/
protected function getValidOptions(): array
{
return [
'limit',
'default',
'null',
'identity',
'scale',
'after',
'update',
'comment',
'signed',
'timezone',
'properties',
'values',
'collation',
'encoding',
'srid',
'seed',
'increment',
'generated',
];
}
/**
* Gets all aliased options. Each alias must reference a valid option.
*
* @return array
*/
protected function getAliasedOptions(): array
{
return [
'length' => 'limit',
'precision' => 'limit',
];
}
/**
* Utility method that maps an array of column options to this objects methods.
*
* @param array<string, mixed> $options Options
* @throws \RuntimeException
* @return $this
*/
public function setOptions(array $options)
{
$validOptions = $this->getValidOptions();
$aliasOptions = $this->getAliasedOptions();
if (isset($options['identity']) && $options['identity'] && !isset($options['null'])) {
$options['null'] = false;
}
foreach ($options as $option => $value) {
if (isset($aliasOptions[$option])) {
// proxy alias -> option
$option = $aliasOptions[$option];
}
if (!in_array($option, $validOptions, true)) {
throw new RuntimeException(sprintf('"%s" is not a valid column option.', $option));
}
$method = 'set' . ucfirst($option);
$this->$method($value);
}
return $this;
}
}

View File

@@ -0,0 +1,237 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Table;
use InvalidArgumentException;
use RuntimeException;
class ForeignKey
{
public const CASCADE = 'CASCADE';
public const RESTRICT = 'RESTRICT';
public const SET_NULL = 'SET NULL';
public const NO_ACTION = 'NO ACTION';
/**
* @var array<string>
*/
protected static $validOptions = ['delete', 'update', 'constraint'];
/**
* @var string[]
*/
protected $columns = [];
/**
* @var \Phinx\Db\Table\Table
*/
protected $referencedTable;
/**
* @var string[]
*/
protected $referencedColumns = [];
/**
* @var string|null
*/
protected $onDelete;
/**
* @var string|null
*/
protected $onUpdate;
/**
* @var string|null
*/
protected $constraint;
/**
* Sets the foreign key columns.
*
* @param string[]|string $columns Columns
* @return $this
*/
public function setColumns($columns)
{
$this->columns = is_string($columns) ? [$columns] : $columns;
return $this;
}
/**
* Gets the foreign key columns.
*
* @return string[]
*/
public function getColumns(): array
{
return $this->columns;
}
/**
* Sets the foreign key referenced table.
*
* @param \Phinx\Db\Table\Table $table The table this KEY is pointing to
* @return $this
*/
public function setReferencedTable(Table $table)
{
$this->referencedTable = $table;
return $this;
}
/**
* Gets the foreign key referenced table.
*
* @return \Phinx\Db\Table\Table
*/
public function getReferencedTable(): Table
{
return $this->referencedTable;
}
/**
* Sets the foreign key referenced columns.
*
* @param string[] $referencedColumns Referenced columns
* @return $this
*/
public function setReferencedColumns(array $referencedColumns)
{
$this->referencedColumns = $referencedColumns;
return $this;
}
/**
* Gets the foreign key referenced columns.
*
* @return string[]
*/
public function getReferencedColumns(): array
{
return $this->referencedColumns;
}
/**
* Sets ON DELETE action for the foreign key.
*
* @param string $onDelete On Delete
* @return $this
*/
public function setOnDelete(string $onDelete)
{
$this->onDelete = $this->normalizeAction($onDelete);
return $this;
}
/**
* Gets ON DELETE action for the foreign key.
*
* @return string|null
*/
public function getOnDelete(): ?string
{
return $this->onDelete;
}
/**
* Gets ON UPDATE action for the foreign key.
*
* @return string|null
*/
public function getOnUpdate(): ?string
{
return $this->onUpdate;
}
/**
* Sets ON UPDATE action for the foreign key.
*
* @param string $onUpdate On Update
* @return $this
*/
public function setOnUpdate(string $onUpdate)
{
$this->onUpdate = $this->normalizeAction($onUpdate);
return $this;
}
/**
* Sets constraint for the foreign key.
*
* @param string $constraint Constraint
* @return $this
*/
public function setConstraint(string $constraint)
{
$this->constraint = $constraint;
return $this;
}
/**
* Gets constraint name for the foreign key.
*
* @return string|null
*/
public function getConstraint(): ?string
{
return $this->constraint;
}
/**
* Utility method that maps an array of index options to this objects methods.
*
* @param array<string, mixed> $options Options
* @throws \RuntimeException
* @return $this
*/
public function setOptions(array $options)
{
foreach ($options as $option => $value) {
if (!in_array($option, static::$validOptions, true)) {
throw new RuntimeException(sprintf('"%s" is not a valid foreign key option.', $option));
}
// handle $options['delete'] as $options['update']
if ($option === 'delete') {
$this->setOnDelete($value);
} elseif ($option === 'update') {
$this->setOnUpdate($value);
} else {
$method = 'set' . ucfirst($option);
$this->$method($value);
}
}
return $this;
}
/**
* From passed value checks if it's correct and fixes if needed
*
* @param string $action Action
* @throws \InvalidArgumentException
* @return string
*/
protected function normalizeAction(string $action): string
{
$constantName = 'static::' . str_replace(' ', '_', strtoupper(trim($action)));
if (!defined($constantName)) {
throw new InvalidArgumentException('Unknown action passed: ' . $action);
}
return constant($constantName);
}
}

View File

@@ -0,0 +1,227 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Table;
use RuntimeException;
class Index
{
/**
* @var string
*/
public const UNIQUE = 'unique';
/**
* @var string
*/
public const INDEX = 'index';
/**
* @var string
*/
public const FULLTEXT = 'fulltext';
/**
* @var string[]|null
*/
protected $columns;
/**
* @var string
*/
protected $type = self::INDEX;
/**
* @var string|null
*/
protected $name;
/**
* @var int|array|null
*/
protected $limit;
/**
* @var string[]|null
*/
protected $order;
/**
* @var string[]|null
*/
protected $includedColumns;
/**
* Sets the index columns.
*
* @param string|string[] $columns Columns
* @return $this
*/
public function setColumns($columns)
{
$this->columns = is_string($columns) ? [$columns] : $columns;
return $this;
}
/**
* Gets the index columns.
*
* @return string[]|null
*/
public function getColumns(): ?array
{
return $this->columns;
}
/**
* Sets the index type.
*
* @param string $type Type
* @return $this
*/
public function setType(string $type)
{
$this->type = $type;
return $this;
}
/**
* Gets the index type.
*
* @return string
*/
public function getType(): string
{
return $this->type;
}
/**
* Sets the index name.
*
* @param string $name Name
* @return $this
*/
public function setName(string $name)
{
$this->name = $name;
return $this;
}
/**
* Gets the index name.
*
* @return string|null
*/
public function getName(): ?string
{
return $this->name;
}
/**
* Sets the index limit.
*
* @param int|array $limit limit value or array of limit value
* @return $this
*/
public function setLimit($limit)
{
$this->limit = $limit;
return $this;
}
/**
* Gets the index limit.
*
* @return int|array|null
*/
public function getLimit()
{
return $this->limit;
}
/**
* Sets the index columns sort order.
*
* @param string[] $order column name sort order key value pair
* @return $this
*/
public function setOrder(array $order)
{
$this->order = $order;
return $this;
}
/**
* Gets the index columns sort order.
*
* @return string[]|null
*/
public function getOrder(): ?array
{
return $this->order;
}
/**
* Sets the index included columns.
*
* @param string[] $includedColumns Columns
* @return $this
*/
public function setInclude(array $includedColumns)
{
$this->includedColumns = $includedColumns;
return $this;
}
/**
* Gets the index included columns.
*
* @return string[]|null
*/
public function getInclude(): ?array
{
return $this->includedColumns;
}
/**
* Utility method that maps an array of index options to this objects methods.
*
* @param array<string, mixed> $options Options
* @throws \RuntimeException
* @return $this
*/
public function setOptions(array $options)
{
// Valid Options
$validOptions = ['type', 'unique', 'name', 'limit', 'order', 'include'];
foreach ($options as $option => $value) {
if (!in_array($option, $validOptions, true)) {
throw new RuntimeException(sprintf('"%s" is not a valid index option.', $option));
}
// handle $options['unique']
if (strcasecmp($option, self::UNIQUE) === 0) {
if ((bool)$value) {
$this->setType(self::UNIQUE);
}
continue;
}
$method = 'set' . ucfirst($option);
$this->$method($value);
}
return $this;
}
}

View File

@@ -0,0 +1,84 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Table;
use InvalidArgumentException;
class Table
{
/**
* @var string
*/
protected $name;
/**
* @var array<string, mixed>
*/
protected $options;
/**
* @param string $name The table name
* @param array<string, mixed> $options The creation options for this table
* @throws \InvalidArgumentException
*/
public function __construct($name, array $options = [])
{
if (empty($name)) {
throw new InvalidArgumentException('Cannot use an empty table name');
}
$this->name = $name;
$this->options = $options;
}
/**
* Sets the table name.
*
* @param string $name The name of the table
* @return $this
*/
public function setName(string $name)
{
$this->name = $name;
return $this;
}
/**
* Gets the table name.
*
* @return string
*/
public function getName(): string
{
return $this->name;
}
/**
* Gets the table options
*
* @return array<string, mixed>
*/
public function getOptions(): array
{
return $this->options;
}
/**
* Sets the table options
*
* @param array<string, mixed> $options The options for the table creation
* @return $this
*/
public function setOptions(array $options)
{
$this->options = $options;
return $this;
}
}

View File

@@ -0,0 +1,122 @@
<?php
/**
* MIT License
* For full license information, please view the LICENSE file that was distributed with this source code.
*/
namespace Phinx\Db\Util;
/**
* Contains all the information for running an ALTER command for a table,
* and any post-steps required after the fact.
*/
class AlterInstructions
{
/**
* @var string[] The SQL snippets to be added to an ALTER instruction
*/
protected $alterParts = [];
/**
* @var (string|callable)[] The SQL commands to be executed after the ALTER instruction
*/
protected $postSteps = [];
/**
* Constructor
*
* @param string[] $alterParts SQL snippets to be added to a single ALTER instruction per table
* @param (string|callable)[] $postSteps SQL commands to be executed after the ALTER instruction
*/
public function __construct(array $alterParts = [], array $postSteps = [])
{
$this->alterParts = $alterParts;
$this->postSteps = $postSteps;
}
/**
* Adds another part to the single ALTER instruction
*
* @param string $part The SQL snipped to add as part of the ALTER instruction
* @return void
*/
public function addAlter(string $part): void
{
$this->alterParts[] = $part;
}
/**
* Adds a SQL command to be executed 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): void
{
$this->postSteps[] = $sql;
}
/**
* Returns the alter SQL snippets
*
* @return string[]
*/
public function getAlterParts(): array
{
return $this->alterParts;
}
/**
* Returns the SQL commands to run after the ALTER instruction
*
* @return (string|callable)[]
*/
public function getPostSteps(): array
{
return $this->postSteps;
}
/**
* Merges another AlterInstructions object to this one
*
* @param \Phinx\Db\Util\AlterInstructions $other The other collection of instructions to merge in
* @return void
*/
public function merge(AlterInstructions $other): void
{
$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(string $alterTemplate, callable $executor): void
{
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);
}
}
}