Migrations
Generated migration format, SchemaBuilder API, and the migration runner.
Table of contents
Generated Migration Format
When you run rowcast-schema diff, a PHP class is generated:
<?php
declare(strict_types=1);
use AsceticSoft\RowcastSchema\Migration\AbstractMigration;
use AsceticSoft\RowcastSchema\Schema\ColumnType;
use AsceticSoft\RowcastSchema\SchemaBuilder\SchemaBuilder;
use AsceticSoft\RowcastSchema\SchemaBuilder\TableBuilder;
final class Migration_20260306_143022_CreateUsersTable extends AbstractMigration
{
public function up(SchemaBuilder $schema): void
{
$schema->createTable('users', function (TableBuilder $table) {
$table->column('id', 'integer')->primaryKey()->autoIncrement();
$table->column('email', 'string'); // default length: 255
$table->column('created_at', ColumnType::Datetime)->default('CURRENT_TIMESTAMP');
});
$schema->addIndex('users', 'idx_users_email', ['email'], unique: true);
}
public function down(SchemaBuilder $schema): void
{
$schema->dropTable('users');
}
}
File naming
Migration files follow the pattern:
Migration_YYYYMMDD_HHMMSS_Description.php
MigrationInterface
Every migration implements:
interface MigrationInterface
{
public function up(SchemaBuilder $schema): void;
public function down(SchemaBuilder $schema): void;
}
The AbstractMigration base class is provided for convenience but carries no logic — it simply implements the interface.
SchemaBuilder API
SchemaBuilder is an operation collector. Calling its methods does not execute SQL directly — it accumulates Operation[] objects that the MigrationRunner later compiles and executes.
Table operations
| Method | Description |
|---|---|
createTable(string $name, callable $callback) |
Define a new table using TableBuilder |
dropTable(string $name) |
Drop a table |
addColumn(string $table, Column $column) |
Add a column |
dropColumn(string $table, string $column) |
Drop a column |
alterColumn(string $table, Column $old, Column $new) |
Alter a column definition |
Index operations
| Method | Description |
|---|---|
addIndex(string $table, string $name, array $columns, bool $unique) |
Create an index |
dropIndex(string $table, string $name) |
Drop an index |
Foreign key operations
| Method | Description |
|---|---|
addForeignKey(string $table, ForeignKey $fk) |
Add a foreign key |
dropForeignKey(string $table, string $name) |
Drop a foreign key |
TableBuilder (Fluent API)
Inside createTable(), use the unified column() API:
$schema->createTable('products', function (TableBuilder $table) {
$table->column('id', 'uuid')->primaryKey();
$table->column('name', 'string'); // default length: 255
$table->column('price', 'decimal')->precision(10, 2)->unsigned();
$table->column('description', 'text')->nullable();
$table->column('created_at', ColumnType::Datetime)->default('CURRENT_TIMESTAMP');
$table->column('active', 'boolean')->default(true);
$table->column('meta', 'jsonb'); // custom raw DB type
});
column() accepts:
ColumnTypeenum values (ColumnType::String,ColumnType::Datetime, …),- known abstract type strings (
'string','integer', …), - custom raw database types (
'jsonb','citext','numeric(20,6)', …).
Raw SQL
When needed, execute arbitrary SQL in migration methods:
public function up(SchemaBuilder $schema): void
{
$schema->sql("UPDATE users SET status = 'active' WHERE status IS NULL");
}
Column modifiers
All return ColumnBuilder (fluent):
| Method | Description |
|---|---|
nullable() |
Allow NULL |
default(mixed) |
Set default value |
primaryKey() |
Mark as primary key |
autoIncrement() |
Enable auto-increment |
unsigned() |
Unsigned integer |
comment(string) |
Column comment |
values(array) |
Enum values |
Migration Runner
MigrationRunner orchestrates migration execution:
- Loads migration files via
MigrationLoader(sorted by timestamp). - Checks applied state via
MigrationRepositoryInterface(queries_rowcast_migrationstable). - Executes
up()ordown()on each migration. - Compiles operations through the SQL
Platformfor the current database driver. - Wraps in a transaction when
Platform::supportsDdlTransactions()returnstrue(PostgreSQL).
Migration State Table
Applied migrations are tracked in _rowcast_migrations (auto-created):
| Column | Type | Description |
|---|---|---|
version |
VARCHAR(255) PK | Migration class name |
applied_at |
DATETIME | When the migration was applied |
Rename Operations
Rename detection is not automatic. The differ cannot distinguish a rename from a drop + create. If you rename a table or column in your schema, the generated migration will contain a drop + create. You can manually edit the migration to use a rename statement if needed.