Migration
Attribute-driven schema-diff engine and SQL migration runner built on the Entity & Database packages.
Point it at your entity metadata, and it will:
Compare the current database schema with your annotated entities.
Emit a timestamped migration class containing portable SQL (up() / down()).
Track every run inside a _migrations table so nothing re-runs accidentally.
1 · Installation
composer require monkeyscloud/monkeyslegion-migration
Depends on the Core, DI, Entity, and Database packages you already installed.
2 · Generating Your First Migration
use MonkeysLegion\Migration\MigrationGenerator;
use MonkeysLegion\Entity\EntityScanner;
use MonkeysLegion\Database\Connection;
$entities = (new EntityScanner([base_path('src/Domain')], $container))->scan();
$generator = new MigrationGenerator(
connection: $container->get(Connection::class),
outDir: base_path('database/migrations'),
);
$path = $generator->generate($entities);
echo "Created $path\n"; // e.g. 2025_06_11_180000_CreatePostsTable.php
The file contains:
final class Migration_2025_06_11_180000
{
public function up(PDO $db): void { /* … create table SQL … */ }
public function down(PDO $db): void { /* … drop table SQL … */ }
}
Tip: Commit generated files—never the _migrations table—to Git so teammates pull the same history.
3 · Running & Rolling Back
# apply everything that hasn’t run yet
php vendor/bin/ml migrate
# step back one batch
php vendor/bin/ml migrate:rollback
# view status
php vendor/bin/ml migrate:status
All commands share one PDO connection, so BEGIN … COMMIT wraps each batch automatically; if any SQL fails the batch rolls back.
4 · Writing Manual Migrations
Need data transforms or raw DDL? Create a class by hand:
// database/migrations/2025_06_11_190000_AddSlugToPosts.php
final class Migration_2025_06_11_190000
{
public function up(PDO $db): void
{
$db->exec('ALTER TABLE posts ADD slug VARCHAR(160) AFTER title');
$db->exec('UPDATE posts SET slug = LOWER(REPLACE(title, " ", "-"))');
}
public function down(PDO $db): void
{
$db->exec('ALTER TABLE posts DROP COLUMN slug');
}
}
The runner picks it up automatically based on filename.
5 · Events Integration
When a batch completes, the package dispatches MigrationEvent (from monkeyslegion-events).
Listeners receive:
MigrationEvent {
public readonly string $direction; // 'up' | 'down'
public readonly array $files; // migration class names
public readonly float $time; // execution time in seconds
}
Perfect for Slack notifications or Prometheus counters.
6 · DI Registration Snippet
return [
MigrationGenerator::class => fn($c) => new MigrationGenerator(
connection: $c->get(MonkeysLegion\Database\Connection::class),
outDir: base_path('database/migrations'),
),
];
The CLI commands resolve both the generator and the database connection from the container, so no duplicated config.
7 · Roadmap
SQLite & PostgreSQL dialects (MySQL 8.4 is supported today).
Column-rename detection (currently treated as drop + add).
migrate:fresh command—drop all tables and re-run head.
License
MIT © 2025 MonkeysCloud