Phinx Documentation
repository·0.x·Indexed 26 days ago
https://github.com/cakephp/phinxPhinx is a lightweight PHP database migration tool for managing database schemas using database-agnostic PHP code. It supports MySQL, PostgreSQL, SQLite, and Microsoft SQL Server. The tool provides CLI commands for initializing configurations, creating and running migrations, rolling back changes with breakpoints, and managing database seeds. It can be installed via Composer or as a Phar archive and can be integrated into Symfony Console applications or PHPUnit tests.
What's inside Phinx
- Phinx is a database migration tool for PHP that allows you to alter and manipulate database schemas using a PHP-based API instead of writing manual SQL. This approach makes migrations portable across different database systems and allows you to version your database schema using a Source Control Management (SCM) system. Phinx tracks which migrations have already been executed to manage the state of your database automatically.
Introduction to Phinx
0.xPhinx is a tool designed to make managing database migrations for PHP applications easy. It focuses strictly on migrations without the overhead of a full database ORM or application framework.
Requirement: Phinx requires a 64-bit version of PHP to function.
Rename a table
0.xTo rename an existing table, use the
rename()method on theTableobject and then callupdate()to commit the change.<?php use Phinx\Migration\AbstractMigration; class MyNewMigration extends AbstractMigration { /** * Migrate Up. */ public function up() { $table = $this->table('users'); $table ->rename('legacy_users') ->update(); } /** * Migrate Down. */ public function down() { $table = $this->table('legacy_users'); $table ->rename('users') ->update(); } }Create a new migration skeleton
0.xTo generate a new migration file, use the
createcommand. Phinx will create a file named with a timestamp prefix (e.g.,YYYYMMDDHHMMSS_my_new_migration.php). If multiple migration paths are configured, you will be prompted to select one.vendor/bin/phinx create MyNewMigrationDrop a table
0.xTo remove a table, call
drop()on theTableobject. You must callsave()to execute the operation. It is recommended to recreate the table in thedown()method to allow for rollbacks.<?php use Phinx\Migration\AbstractMigration; class MyNewMigration extends AbstractMigration { public function up() { $this->table('users')->drop()->save(); } public function down() { $users = $this->table('users'); $users->addColumn('username', 'string', ['limit' => 20]) ->addColumn('password', 'string', ['limit' => 40]) ->addColumn('password_salt', 'string', ['limit' => 40]) ->addColumn('email', 'string', ['limit' => 100]) ->addColumn('first__name', 'string', ['limit' => 30]) ->addColumn('last_name', 'string', ['limit' => 30]) ->addColumn('created', 'datetime') ->addColumn('updated', 'datetime', ['null' => true]) ->addIndex(['username', 'email'], ['unique' => true]) ->save(); } }Retrieve results from a Query
0.xAfter executing a query, you can retrieve the data in two primary ways:
- Iterate directly: The builder object can be used in a
foreachloop. - Fetch all: Call
execute()to get a statement object, then usefetchAll('assoc')to get an associative array.
<?php // Option 1: Iterate foreach ($builder as $row) { echo $row['title']; } // Option 2: Fetch all results $results = $builder->execute()->fetchAll('assoc');- Iterate directly: The builder object can be used in a
Build SELECT queries with the Query Builder
0.xUse the Select Query Builder to fetch data. You can specify fields, use aliases, and apply complex
WHEREconditions using arrays or closures.Selecting Fields
- Pass an array of column names:
select(['id', 'title']) - Use associative arrays for aliasing:
select(['alias' => 'column_name']) - Use a closure for dynamic selection.
Where Conditions
- Simple arrays:
where(['id' => 1])orwhere(['id >' => 1]). You can include operators like>,<, etc., in the key string. - Logical operators: Use
andWhere()for chaining or pass an'OR'key in the array:where(['OR' => ['id >' => 1, 'title' => 'My title']]). - Closures and Expressions: For complex logic, pass a closure that receives an expression object (
$exp).
<?php // Selecting with aliases $builder->select(['pk' => 'id', 'aliased_title' => 'title', 'body']); // Simple WHERE $builder->where(['id >' => 1])->andWhere(['title' => 'My Title']); // Complex WHERE with expression object $builder->select('*') ->from('articles') ->where(function ($exp) { return $exp ->eq('author_id', 2) ->gt('view_count', 10); });- Pass an array of column names:
Install Phinx as a Phar archive
0.xYou can build Phinx as a Phar archive using the Box application. This is useful if you want a standalone executable.Configure Phinx using PHP, YAML, JSON, or YAML
0.xPhinx supports multiple configuration formats:
yaml,yml,json, andphp. When running Phinx commands, you can specify a custom file using the--configurationflag. If not specified, Phinx searches forphinx.php,phinx.json,phinx.yml, orphinx.yamlin the current directory.Using a PHP configuration file: When using a
.phpfile, the file isincludedrather than parsed. It mustreturnan array of configuration items. This allows you to pass an existing PDO instance via theconnectionkey, which is useful for sharing connections with your application. Note that you must still explicitly provide thename(database name) as Phinx cannot infer it from the PDO instance.$app = require 'app/phinx.php'; $pdo = $app->getDatabase()->getPdo(); return [ 'environments' => [ 'default_environment' => 'development', 'development' => [ 'name' => 'devdb', 'connection' => $pdo ] ] ];Use Phinx within PHPUnit tests
0.xYou can programmatically use Phinx to prepare or seed your database during unit tests.
To run migrations via the
PhinxApplicationclass, instantiate it, setsetAutoExit(false)to prevent the application from terminating the process, and use therun()method with aStringInputcommand (e.g.,'migrate').If you are using an in-memory database (like SQLite
:memory:), you must provide a specificPDOinstance to Phinx via the configuration to ensure the test runner and Phinx share the same connection.public function setUp() { $app = new PhinxApplication(); $app->setAutoExit(false); $app->run(new StringInput('migrate'), new NullOutput()); }Create a new table
0.xTo create a table, use the
table()method to initialize the object, chainaddColumn()andaddIndex()calls to define the schema, and finally callcreate()to commit the changes.Phinx automatically creates an auto-incrementing primary key column named
id.Customizing Primary Keys
- Disable automatic ID: Pass
['id' => false]in the options array to thetable()method. If you disableid, you must specify aprimary_key(or array of columns) to avoid having no primary key. - Change ID name: Pass
['id' => 'your_custom_name']to thetable()method. - Composite Primary Key: Pass an array of column names to the
primary_keyoption:['primary_key' => ['col1', 'col2']].
<?php use Phinx\Migration\AbstractMigration; class MyNewMigration extends AbstractMigration { public function change() { $users = $this->table('users'); $users->addColumn('username', 'string', ['limit' => 20]) ->addColumn('password', 'string', ['limit' => 40]) ->addColumn('password_salt', 'string', ['limit' => 40]) ->addColumn('email', 'string', ['limit' => 100]) ->addColumn('first_name', 'string', ['limit' => 30]) ->addColumn('last_name', 'string', ['limit' => 30]) ->addColumn('created', 'datetime') ->addColumn('updated', 'datetime', ['null' => true]) ->addIndex(['username', 'email'], ['unique' => true]) ->create(); } }- Disable automatic ID: Pass
Use the change() method for reversible migrations
0.xThe
change()method is the default for writing reversible migrations. When using the Table API withinchange(), Phinx automatically determines how to undo actions like creating/renaming tables, adding columns/indexes, or adding foreign keys.Important:
- When working with the Table class inside
change(), you must usecreate()orupdate()instead ofsave()to ensure Phinx can track the operation for reversal. - If an action cannot be automatically reversed, Phinx will throw an
IrreversibleMigrationExceptionduring a rollback. To handle non-reversible logic (like inserting data) withinchange(), wrap it in a conditional check using$this->isMigratingUp().
<?php use Phinx\ Migration\\AbstractMigration; class CreateUserLoginsTable extends AbstractMigration { public function change() { // create the table $table = $this->table('user_logins'); $table->addColumn('user_id', 'integer') ->addColumn('created', 'datetime') ->create(); if ($this->isMigratingUp()) { $table->insert([['user_id' => 1, 'created' => '2020-01-19 03:14:07']]) ->save(); } } }- When working with the Table class inside