migralign / laravel-migralign
MigrAlign — align live MySQL tables with Laravel migration schema, safely.
Fund package maintenance!
Requires
- php: ^8.2
- illuminate/console: ^11.0|^12.0|^13.0
- illuminate/database: ^11.0|^12.0|^13.0
- illuminate/support: ^11.0|^12.0|^13.0
- laravel/prompts: ^0.1.24|^0.3|^0.4
Requires (Dev)
- orchestra/testbench: ^9.0|^10.0|^11.0
- phpunit/phpunit: ^11.0
README
MigrAlign
Align your database with your migrations.
Detect schema drift, add missing columns, auto-apply safe updates, and get guided confirmation for risky changes.
composer require migralign/laravel-migralign
Repository · Issues · Security
Table of contents
- Overview
- Features
- Requirements
- Installation
- Quick start
- How MigrAlign works
- Risk model and behavior
- Command reference
- Configuration
- Recommended workflows
- Examples
- Limitations
- Troubleshooting
- FAQ
- Contributing
- License
Overview
MigrAlign compares your Laravel migration-defined schema with the current MySQL/MariaDB schema and helps you synchronize differences safely.
It is useful when:
- schema drift appears between migration files and real database state
- teams edit migrations and need a deterministic sync pass
- you want safe automation for additive/low-risk changes with explicit handling for risky changes
Features
- Scans migration intent from
Schema::create()andSchema::table() - Introspects live schema from
information_schema - Computes table/column diffs
- Auto-applies safe changes
- Interactively prompts for risky/destructive changes
- Supports targeted sync by table or migration filename
- Supports dry-run preview and force mode
- Prints final sync report (
applied,skipped,pending manual,errors)
Requirements
- PHP 8.2+
- Laravel 11, 12, or 13
- MySQL or MariaDB connection
- Laravel 13 requires PHP 8.3+ (per Laravel 13 requirements)
Installation
Install the package:
composer require migralign/laravel-migralign
Laravel auto-discovers the service provider; no manual registration is required.
Publish config (optional):
php artisan vendor:publish --tag=migralign-config
This creates:
config/migralign.php
Quick start
- Update your migration files.
- Preview what would change:
php artisan migralign:sync --dry-run
- Apply sync:
php artisan migralign:sync
How MigrAlign works
- Scan migration files from
config('migralign.migrations_path') - Build expected schema intent from migration
up()logic - Read live schema from MySQL
information_schema - Compute diff for tables and columns
- Classify risk for each change
- Apply safe changes automatically (if enabled)
- Prompt for risky changes (or skip/apply/abort)
- Print report
Risk model and behavior
MigrAlign classifies each change into one of:
saferiskydestructive
Usually safe
- add nullable columns
- widen certain column sizes
- non-destructive metadata-level changes
Usually risky
- shrinking type/length (example:
VARCHAR(255)->VARCHAR(50)) - nullable -> not nullable
- enum contractions
- type family changes with potential conversion problems
Usually destructive
- drop column
- drop table
For risky/destructive changes, MigrAlign shows:
- why the change is risky
- optional pre-check query
- suggested remediation SQL/instructions
- interactive choice: apply / skip / abort
Command reference
Main command
php artisan migralign:sync
Options
-
--dry-run
Show planned changes only, do not apply. Always exits with status0, even when differences are found. -
--force
Apply risky/destructive changes without interactive prompts. Also skips pre-check blocking when existing rows would violate the new schema. Use only when you explicitly accept that data risk. -
--table=users
Limit sync to a specific table. -
--migration=create_users
Scan only migrations with matching filename text. -
--connection=mysql
Override DB connection used for introspection/apply.
Typical command examples
# Preview everything php artisan migralign:sync --dry-run # Sync only users table php artisan migralign:sync --table=users # Preview a subset of migrations php artisan migralign:sync --dry-run --migration=2024_01_01 # Apply all including risky (no prompts; bypasses pre-check blocking) php artisan migralign:sync --force
Configuration
Default config file:
return [ 'migrations_path' => database_path('migrations'), 'ignored_tables' => [ 'migrations', 'password_reset_tokens', 'sessions', 'cache', 'cache_locks', 'jobs', 'job_batches', 'failed_jobs', ], 'auto_apply_safe' => true, 'connection' => null, ];
Config keys
-
migrations_path
Path to migration files used to build expected schema. -
ignored_tables
Tables excluded from diffing. -
auto_apply_safe
Iftrue, safe changes apply automatically. -
connection
Default DB connection.nullmeans Laravel default.
Recommended workflows
Workflow A: Add a new column
When you add a column in migrations (Schema::create() or Schema::table()):
php artisan migralign:sync --dry-run php artisan migralign:sync
If the column is nullable or otherwise safe, it is created automatically.
Workflow B: Risky schema tightening
For changes like nullable(true) -> nullable(false) or length shrink:
- run
--dry-run - review pre-checks and remediation
- backfill/clean data if needed
- run sync and confirm prompts
Workflow C: Preview in CI or before deploy
Use dry-run locally or in pipelines to review planned changes:
php artisan migralign:sync --dry-run
--dry-run always exits successfully (status 0), even when drift is detected. For CI gates that must fail on drift, parse the command output or add your own wrapper check.
Examples
Example 1: New nullable column
Migration:
Schema::table('users', function (Blueprint $table) { $table->string('phone', 20)->nullable(); });
Result:
- Diff includes
add_columnforusers.phone - Sync applies it automatically (safe)
Example 2: Column removed from migration intent
If a column exists in DB but not in migration intent:
- diff includes
drop_column - classified as destructive
- prompt asks whether to apply/skip/abort
Example 3: Not-null tightening
Migration intent changes nullable column to not nullable:
- diff includes
modify_column - risky classification
- pre-check identifies violating rows when applicable
Limitations
- Current support target is MySQL/MariaDB only
- Migration scanning executes migration
up()logic in recording mode; extremely dynamic migration logic may need extra care - Destructive actions should always be reviewed in dry-run first
Troubleshooting
"supports MySQL/MariaDB only"
Your selected connection is not MySQL/MariaDB.
Use --connection= with a MySQL connection, or set migralign.connection.
No differences found but you expect changes
- verify migration file is inside
migralign.migrations_path - verify
--migration=filter is not excluding it - ensure target table is not in
ignored_tables
Risky change is blocked or skipped
- run with
--dry-runfirst - follow remediation instructions
- fix data inconsistencies
- run again after fixing data, or use
--forceonly if you explicitly accept bypassing prompts and pre-check blocking
FAQ
Should I edit old historical migrations?
Prefer adding new migrations for forward changes.
Editing old migrations can create large drift expectations across environments.
Is this a replacement for php artisan migrate?
No. migrate applies migration history; MigrAlign aligns current schema intent to live schema through diff + risk controls.
Can I use this in production?
Yes, but always run dry-run first and review destructive/risky operations.
Contributing
Contributions are welcome. See CONTRIBUTING.md for setup, test commands, and pull request guidelines.
Report security issues privately — see SECURITY.md.
License
MIT
