Initial Setup
Installing Phinx
composer require robmorgan/phinx
Creating the Configuration File
Run the initialization command to generate the configuration:
vendor/bin/phinx init
This creates a phinx.php file in your project root. Within this configuration:
pathsdefines the directories for migration and seed filesenvironmentscontains database connection settings for different contexts (development, staging, production)default_environmentspecifies which configuration to use when no environment flag is provided
Environment Variables Integration
Using .env files to centralize database credentials is a common practice. Since Phinx executes outside of framework contexts, Laravel's auto-loading won't work. Install the dotenv library separately:
composer require vlucas/phpdotenv
Configure phinx.php as follows:
<?php
$dotenv = Dotenv\Dotenv::createImmutable(__DIR__);
$dotenv->load();
return [
"paths" => [
"migrations" => "database/migrations",
"seeds" => "database/seeds"
],
"environments" => [
"default_migration_table" => "phinxlog",
"default_environment" => "production",
"production" => [
"adapter" => "mysql",
"host" => $_SERVER['DB_HOST'],
"name" => $_SERVER['DB_NAME'],
"user" => $_SERVER['DB_USER'],
"pass" => $_SERVER['DB_PASS'],
"port" => $_SERVER['DB_PORT'],
"charset" => $_SERVER['DB_CHARSET']
]
]
];
Command Reference
Generating Migration Files
vendor/bin/phinx create MigrationName
Phinx automatically generates filenames with timestamps. Follow these naming conventions:
- New tables:
Create+ TableName →CreateUsersTable - Table modifications:
Modify+ TableName + Description →ModifyUsersAddStatus - Table deletion:
Delete+ TableName →DeleteOldLogsTable
Phinx converts PascalCase to snake_case for filenames. For instance, CreateUsersTable becomes 20251028123045_create_users_table.php.
Runing Migrations
vendor/bin/phinx migrate
Available flags:
-e: Target environment (defaults toproduction)-t: Target version number—migrations run sequentially up to this version if not provided--dry-run: Display SQL statements without executing them
Setting Breakpoints
After deploying migrations to production, set a breakpoint immediately to prevent accidental mass rollbacks:
vendor/bin/phinx breakpoint
Options:
-e: Environment name-t: Version number-r: Remove the breakpoint
Rolling Back Migrations
Warning: Rollbacks permanently delete database structures and all associated data.
vendor/bin/phinx rollback
Options:
-e: Environment name-t: Specific version (or0to rollback everything)-d: Rollback by date (YYYYmmddHHiiss)-f: Force rollback ignoring breakpoints--dry-run: Display SQL without executing
Checking Migration Status
vendor/bin/phinx status
The -e flag specifies the environment.
Creating and Running Seeders
Generate a seeder:
vendor/bin/phinx seed:create SeederName
Recommended naming pattern: table name followed by description, e.g., UsersAddDemoDataSeeder.
Execute seeders:
vendor/bin/phinx seed:run
Options:
-e: Target environment-s: Specific seeder class (can be specified multiple times)
Migration Script Reference
Supported Column Types
| Phinx Type | MySQL Type | Required Parameters | Description |
|---|---|---|---|
smallinteger |
SMALLINT | — | Small integer |
integer |
INT | — | Standard integer |
biginteger |
BIGINT | — | Large integer |
float |
FLOAT | — | Single precision |
double |
DOUBLE | — | Double precision |
decimal |
DECIMAL(M,D) | precision, scale |
Fixed-point (monetary values) |
bit |
BIT | — | Bit field |
boolean |
TINYINT(1) | — | Boolean (0/1) |
char |
CHAR(n) | limit |
Fixed-length string |
string |
VARCHAR(n) | limit |
Variable-length string |
text |
TEXT | — | Large text |
enum |
ENUM(...) | values |
Enumerated values |
set |
SET(...) | values |
Set values |
uuid |
CHAR(36) | — | UUID string |
date |
DATE | — | Date only |
time |
TIME | — | Time only |
datetime |
DATETIME | — | Date and time |
timestamp |
TIMESTAMP | — | Timestamp |
binary |
BINARY(n) | limit |
Fixed-length binary |
blob |
BLOB | — | Binary large object |
tinyblob |
TINYBLOB | — | Small binary |
mediumblob |
MEDIUMBLOB | — | Medium binary |
longblob |
LONGBLOB | — | Large binary |
json |
JSON | — | JSON data |
Common Column Parameters
| Parameter | Type | Description |
|---|---|---|
limit / length |
int | Length or byte limit |
default |
mixed | Default value |
null |
bool | Allow NULL values |
after |
string | Position after column (or MysqlAdapter::FIRST) |
comment |
string | Column comment |
precision |
int | Total decimal digits |
scale |
int | Decimal places |
signed |
bool | Allow negative values |
values |
array/string | Enum/set options |
identity |
bool | Enable auto-increment (requires null: false) |
Migration Code Examples
<?php
declare(strict_types=1);
use Phinx\Db\Adapter\MysqlAdapter;
use Phinx\Migration\AbstractMigration;
final class CreateProductsTable extends AbstractMigration
{
public function change(): void
{
// create() applies to new tables
// update() applies to existing tables
// Always save the table object at the end
// Check table existence: $this->hasTable('table_name');
// Drop table: $this->table('table_name')->drop()->save();
// Initialize table with primary key and metadata
$products = $this->table('products', [
'id' => 'product_id',
'comment' => 'Products catalog',
'engine' => 'InnoDB',
'collation' => 'utf8mb4_unicode_ci'
]);
// Configuration options:
// id: Primary key column name (string), false (no auto PK), or omit (default 'id')
// comment: Table description
// row_format: MySQL row format (DYNAMIC, COMPACT, REDUNDANT, COMPRESSED)
// engine: Storage engine (InnoDB, MyISAM, MEMORY)
// collation: Character set collation
// signed: Allow negative integers
// limit: Maximum primary key length
// Modify table metadata
$products->changeComment('Updated products catalog');
$products->changePrimaryKey(['new_primary_id']);
$products->rename('renamed_products');
// Check column existence: $table->hasColumn('column_name');
// Rename and remove columns
$products->renameColumn('old_name', 'new_name');
$products->removeColumn('deprecated_field');
// Add columns with fluent interface
$products
->addColumn('sku', 'string', ['limit' => 64, 'null' => false])
->addColumn('thumbnail', 'string', ['comment' => 'Product image', 'null' => true, 'after' => 'sku'])
->addColumn('registered_at', 'integer', [
'comment' => 'Registration timestamp',
'null' => false,
'limit' => MysqlAdapter::INT_BIG
]);
// Integer sizes via limit parameter:
// smallinteger (SMALLINT): 2 bytes
// integer (INT): 4 bytes
// biginteger (BIGINT): 8 bytes
// Use MysqlAdapter::INT_SMALL, MysqlAdapter::INT_REGULAR, MysqlAdapter::INT_BIG
// Numeric types
// float(FLOAT), double(DOUBLE)
// decimal(DECIMAL): requires precision and scale
// bit(BIT), boolean(TINYINT(1))
// String types
// char(CHAR): fixed length via limit
// string(VARCHAR): variable length via limit
// text(TEXT)
// enum(...): requires values parameter
// set(...): requires values parameter
// uuid(CHAR(36))
// Date/time types
// date(DATE), time(TIME), datetime(DATETIME), timestamp(TIMESTAMP)
// Binary types
// binary(BINARY): fixed length
// blob(BLOB), tinyblob(TINYBLOB), mediumblob(MEDIUMBLOB), longblob(LONGBLOB)
// json(JSON)
// Column modifiers
// limit/length: int
// default: mixed
// null: bool (true/false)
// after: column name or MysqlAdapter::FIRST
// comment: string
// precision: int (total digits)
// scale: int (decimal places)
// signed: bool
// values: array or comma-separated string
// identity: bool (auto-increment, requires null: false)
// Modify existing columns
$products->changeColumn('price', 'decimal', [
'precision' => 10,
'scale' => 2,
'null' => false
]);
// Index management
$products->addIndex(['sku'], ['unique' => true]);
$products->addIndex(['registered_at']);
// Remove indexes
$products->removeIndex(['sku']);
$products->removeIndexByName('idx_registered_at');
}
}
Seeder Implementation
<?php
declare(strict_types=1);
use Phinx\Seed\AbstractSeed;
class ProductsSeedData extends AbstractSeed
{
public function run(): void
{
$dataset = [
[
'name' => 'Sample Product',
'created_at' => date('Y-m-d H:i:s'),
],
[
'name' => 'Another Item',
'created_at' => date('Y-m-d H:i:s'),
]
];
$productsTable = $this->table('products');
$productsTable->insert($dataset)->saveData();
// Clear all rows from table
$productsTable->truncate();
}
}
Generating Migrations from Existing Databases
For projects transitioning to Phinx, the phinx-migrations-generator tool reverse-engineers migrations from an existing database:
composer require odan/phinx-migrations-generator --dev
The tool reads your existing phinx.php configurasion automatical. Generate migrations with:
vendor/bin/phinx-migrations generate
This produces migration files representing your current database structure, ready for version control and deployment to other environments.