PHPackages                             erimeilis/laravel-migrations-drift - PHPackages - PHPackages  [Skip to content](#main-content)[PHPackages](/)[Directory](/)[Categories](/categories)[Trending](/trending)[Leaderboard](/leaderboard)[Changelog](/changelog)[Analyze](/analyze)[Collections](/collections)[Log in](/login)[Sign up](/register)

1. [Directory](/)
2. /
3. erimeilis/laravel-migrations-drift

ActiveLibrary

erimeilis/laravel-migrations-drift
==================================

Detect and fix migration table drift in Laravel apps. Sync filenames, compare schemas, rename migrations.

v0.3.5(1mo ago)2154↓61%MITPHPPHP ^8.2CI passing

Since Feb 25Pushed 1mo agoCompare

[ Source](https://github.com/erimeilis/laravel-migrations-drift)[ Packagist](https://packagist.org/packages/erimeilis/laravel-migrations-drift)[ RSS](/packages/erimeilis-laravel-migrations-drift/feed)WikiDiscussions main Synced 1mo ago

READMEChangelog (1)Dependencies (8)Versions (9)Used By (0)

Laravel Migrations Drift
========================

[](#laravel-migrations-drift)

**Keep your Laravel database in perfect sync with your migrations**

[![Latest Version on Packagist](https://camo.githubusercontent.com/5b33149d278d705eade758bcb24232b51ad500aee453362d6757becd647828e3/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f762f6572696d65696c69732f6c61726176656c2d6d6967726174696f6e732d64726966742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/erimeilis/laravel-migrations-drift)[![Total Downloads](https://camo.githubusercontent.com/2633555cbb3eea9b61e909e394840417d35dc5c6fb86fec3879d82a97fb86f7f/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f64742f6572696d65696c69732f6c61726176656c2d6d6967726174696f6e732d64726966742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/erimeilis/laravel-migrations-drift)[![Tests](https://github.com/erimeilis/laravel-migrations-drift/actions/workflows/tests.yml/badge.svg)](https://github.com/erimeilis/laravel-migrations-drift/actions/workflows/tests.yml)[![PHP Version](https://camo.githubusercontent.com/d1cd596758d165cdc92c3e4fb2deecb9941d932d3b41008a403113679a09de7d/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f7068702d762f6572696d65696c69732f6c61726176656c2d6d6967726174696f6e732d64726966742e7376673f7374796c653d666c61742d737175617265)](https://packagist.org/packages/erimeilis/laravel-migrations-drift)[![Laravel 11+](https://camo.githubusercontent.com/1286b48734ed4a3d3b911ef4f28a0242f75b8f1b66672032bd0bec94cd49ba1d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f4c61726176656c2d313125323025374325323031322d4646324432302e7376673f7374796c653d666c61742d737175617265)](https://laravel.com)[![License: MIT](https://camo.githubusercontent.com/e177309cd473d17d6e043b3ae63b1e1e5bfc9e31fc20d7244d6c44caf7e82306/68747470733a2f2f696d672e736869656c64732e696f2f7061636b61676973742f6c2f6572696d65696c69732f6c61726176656c2d6d6967726174696f6e732d64726966742e7376673f7374796c653d666c61742d737175617265)](https://opensource.org/licenses/MIT)

> 🔍 Detect every kind of schema drift — filenames, columns, indexes, foreign keys 🔧 Fix it all — schema-aware record sync, corrective migrations, consolidation 🛡️ Safe by default — dry-run mode, automatic backups, transactional operations 🎯 Interactive prompts — powered by `laravel/prompts` for a beautiful CLI experience

---

✨ Features
----------

[](#sparkles-features)

### 🔍 Schema-Aware Detection

[](#mag-schema-aware-detection)

- ✅ **6-State Classification** — Every migration is classified as OK, Bogus Record, Missing File, Orphan Record, Lost Record, or New Migration
- 🗄️ **Schema Verification** — Checks actual DB schema to determine if migrations truly ran, not just if records exist
- 🔬 **Code Quality** — AST-parses migrations to detect missing `down()` methods, empty migrations, and more
- 📊 **JSON Output** — Machine-readable output for CI pipelines with exit code `0`/`1`

### 🔧 Unified Fix Command

[](#wrench-unified-fix-command)

- 🧠 **Schema-Aware Sync** — Cross-references files, DB records, and actual schema to make correct decisions
- 🚧 **Schema Repair** — Generates corrective migration files for missing tables, columns, indexes, and FKs
- 🔗 **Consolidation** — Merges redundant per-table migration chains into single clean migrations via AST replay

### 🛡️ Safety First

[](#shield-safety-first)

- 🔒 **Dry-Run Default** — Every command shows what would change before doing anything
- 💾 **Automatic Backups** — JSON snapshots before any destructive operation
- ♻️ **One-Command Restore** — Roll back to last backup instantly
- 🔄 **Transactional Operations** — DB changes wrapped in transactions with rollback on failure
- 📁 **Atomic File Operations** — Archive-based consolidation with full rollback on error

### 🎯 Developer Experience

[](#dart-developer-experience)

- 🎨 **Interactive Prompts** — Beautiful multi-select, confirmations, and spinners via `laravel/prompts`
- 🔌 **Multi-Connection** — Works with any database connection, not just the default
- ⚡ **CI-Ready** — Non-interactive mode with JSON output for automated pipelines
- 🧪 **230 Tests** — Comprehensive test suite with PHPStan level 6 static analysis

---

📦 Installation
--------------

[](#package-installation)

```
composer require erimeilis/laravel-migrations-drift --dev
```

The service provider is auto-discovered. To publish the config:

```
php artisan vendor:publish --tag=migration-drift-config
```

**Requirements:**

- PHP 8.2 or higher
- Laravel 11.x or 12.x
- `CREATE DATABASE` permission for schema comparison (optional — gracefully skipped if unavailable)

---

🚀 Quick Start
-------------

[](#rocket-quick-start)

```
# 1. Detect all drift in one pass
php artisan migrations:detect

# 2. Preview what fix would do (dry-run, safe)
php artisan migrations:fix

# 3. Apply fixes (creates backup first)
php artisan migrations:fix --force

# 4. Run any genuinely new migrations
php artisan migrate --force

# 5. Consolidate redundant migration chains
php artisan migrations:fix --consolidate --force
```

---

📚 Commands
----------

[](#books-commands)

### 🔍 `migrations:detect`

[](#mag-migrationsdetect)

Performs a comprehensive three-layer analysis in one pass:

1. **State classification** — classifies every migration into one of 6 states by cross-referencing files, DB records, and actual schema
2. **Schema drift** — creates a temp database, runs all migrations, diffs the resulting schema against your actual database
3. **Code quality** — parses migration ASTs to detect structural issues

```
php artisan migrations:detect
php artisan migrations:detect --json              # machine-readable output
php artisan migrations:detect --connection=mysql2  # specific connection
```

FlagDescription`--json`Output results as JSON`--connection=`Database connection to use`--path=`Override migrations directory**Exit codes:** `0` = no drift, `1` = drift detected.

**Example output:**

```
3 migration(s) OK.
1 new migration(s) pending (will run with `php artisan migrate`).

Bogus records (registered but never ran):
  2026_01_15_000001_create_widgets_table

Lost records (ran but not registered):
  2026_01_01_000003_add_bio_to_users_table

Schema comparison: no differences found.

DRIFT DETECTED

```

#### Migration States

[](#migration-states)

StateMeaningAction taken by `fix --force`**OK**Record + file + schema all matchNone**Bogus Record**Record + file exist, but schema says it never ranDelete record**Missing File**Record + schema exist, but file is goneWarn (file can't be auto-regenerated)**Orphan Record**Record exists, no file, no schema evidenceDelete record**Lost Record**File + schema exist, but no DB recordInsert record**New Migration**File exists, no record, not in schema yetLeft alone — `php artisan migrate` will run it> **Note:** Schema comparison requires `CREATE DATABASE` permission to create and drop a temporary database. If unavailable, it degrades gracefully with a warning.

---

### 🔧 `migrations:fix`

[](#wrench-migrationsfix)

The unified repair command. Analyzes all migrations against the actual database schema, then fixes bookkeeping and generates corrective migrations.

```
php artisan migrations:fix                  # dry-run: shows classified states
php artisan migrations:fix --force          # apply fixes (creates backup first)
php artisan migrations:fix --consolidate    # dry-run: shows consolidation candidates
php artisan migrations:fix --restore        # restore from latest backup
```

#### How It Works

[](#how-it-works)

1. **Classify** — Every migration is classified into one of 6 states (see table above)
2. **Fix bookkeeping** — In a single transaction: delete bogus/orphan records, insert lost records
3. **Schema repair** — Compare actual schema against what migrations produce, generate corrective migrations for any remaining drift
4. **New migrations** are never touched — they're left for `php artisan migrate`

**Dry-run output:**

```
3 migration(s) OK.
1 new migration(s) pending (will run with `php artisan migrate`).

Bogus record (registered but never ran):
  2026_01_15_000001_create_widgets_table

Lost record (ran but not registered):
  2026_01_01_000003_add_bio_to_users_table

DRY RUN — use --force to apply changes.

```

**After `--force`:**

```
Backup created: storage/migrations-drift/backup-2026-02-27-143022.json

Bookkeeping fixed:
  Removed 1 bogus record(s).
  Inserted 1 lost record(s).

Schema is in sync — no corrective migrations needed.

```

**Idempotent:** Running again after fix outputs `Everything in sync — no fixes needed.`

#### 🔗 Consolidation (`--consolidate`)

[](#link-consolidation---consolidate)

Parses migration ASTs with [nikic/php-parser](https://github.com/nikic/PHP-Parser) to find tables with multiple migrations that can be merged into a single clean migration.

```
php artisan migrations:fix --consolidate        # dry-run: shows candidates
php artisan migrations:fix --consolidate --force # consolidate selected tables
```

Handles column additions, drops, index changes, and foreign keys. Multi-table migrations are automatically skipped from consolidation to preserve safety.

#### ♻️ Restore from Backup

[](#recycle-restore-from-backup)

```
php artisan migrations:fix --restore
```

FlagDescription`--force`Apply changes (default is dry-run)`--restore`Restore migrations table from latest backup`--consolidate`Consolidate redundant migrations per table`--connection=`Database connection to use`--path=`Override migrations directory---

### ✏️ `migrations:rename`

[](#pencil2-migrationsrename)

Renames migration files to use a target date prefix with sequential numbering. Updates both files and the migrations table atomically — DB records first (transactional), files second, with automatic rollback on failure.

```
php artisan migrations:rename                           # dry-run
php artisan migrations:rename --force --date=2026-02-25  # apply
```

FlagDescription`--force`Apply renames`--date=YYYY-MM-DD`Target date prefix (default: today)`--connection=`Database connection to use`--path=`Override migrations directory**Example output:**

```
 Current                                    | New
 0001_01_01_000000_create_users_table.php   | 2026_02_25_000001_create_users_table.php
 0001_01_01_000001_create_cache_table.php   | 2026_02_25_000002_create_cache_table.php

Would rename 2 files.
DRY RUN — use --force to apply.

```

Files that already match the target pattern are skipped.

---

⚙️ Configuration
----------------

[](#gear-configuration)

```
php artisan vendor:publish --tag=migration-drift-config
```

```
// config/migration-drift.php

return [
    // Where to store migrations table backups
    'backup_path' => storage_path('migrations-drift'),

    // Maximum number of backup files to keep (oldest rotated out)
    'max_backups' => 5,

    // Path to migration files
    'migrations_path' => database_path('migrations'),

    // Database connection (null = default connection)
    'connection' => null,
];
```

---

🚢 Production Workflow
---------------------

[](#ship-production-workflow)

```
# 1. Deploy new code

# 2. Preview what fix will do (dry-run, safe)
php artisan migrations:fix

# 3. Review the output, then apply (creates backup first)
php artisan migrations:fix --force

# 4. Run any genuinely new migrations
php artisan migrate --force

# 5. Verify no drift remains
php artisan migrations:detect
```

If something goes wrong after step 3:

```
php artisan migrations:fix --restore
```

---

🤖 CI Integration
----------------

[](#robot-ci-integration)

Add drift detection to your CI pipeline:

```
- name: Check migration drift
  run: php artisan migrations:detect --json
```

The command exits with code `1` when drift is detected, failing the pipeline. JSON output includes structured `migration_states` with per-migration classification, `schema_drift`, and `quality_issues`.

---

🛡️ Safety Matrix
----------------

[](#shield-safety-matrix)

ScenarioBehaviorAny fix without `--force`Dry-run. Shows classified states and planned actions, changes nothing.`--force` on fixBacks up migrations table, then fixes bookkeeping in a transaction + generates corrective migrations.`--force` run again"Everything in sync" — idempotent, no changes.Fix broke something`--restore` loads the last backup.`detect`Read-only. Main database is never modified.Schema comparisonCreates and drops a temp database. Main database is read-only.`rename --force`Updates DB records first (transactional), then renames files. Rolls back DB on file failure.ConsolidationArchives original files atomically. Rolls back on any failure.New migrations detectedLeft alone. `php artisan migrate` handles them normally.Backups are stored as JSON in `storage/migrations-drift/`. The last 5 are kept by default.

---

🔍 How It Works
--------------

[](#mag-how-it-works)

### 🧠 6-State Classification

[](#brain-6-state-classification)

The core innovation: every migration is classified by cross-referencing three data sources:

```
                    Has DB Record?
                   /              \
                 YES               NO
                /                    \
          Has File?              Has File?
         /        \             /        \
       YES         NO        YES         NO
        |           |          |          (impossible)
  Schema says   Schema has   Schema says
   applied?     evidence?     applied?
    /    \       /    \       /    \
  YES    NO    YES    NO    YES    NO
   |      |     |      |     |      |
  OK   BOGUS  MISS-  ORPHAN LOST   NEW
       RECORD  ING   RECORD RECORD MIGR.
               FILE

```

### 🚧 Architecture

[](#construction-architecture)

```
migrations:detect / migrations:fix
       |
       v
 +------------------------+
 | MigrationStateAnalyzer |   DELETE/INSERT in transaction
       |
       +-- fixSchemaDrift --> SchemaComparator --> MigrationGenerator --> .php files
       |
       +-- --consolidate --> MigrationParser --> AST replay --> single migration

```

### 🔧 Key Components

[](#wrench-key-components)

- **MigrationStateAnalyzer** — The brain: classifies every migration into one of 6 states using files, DB records, and actual schema
- **SchemaComparator** — Creates a temporary database, runs all migrations on it, then diffs both schemas column-by-column
- **MigrationParser** + **MigrationVisitor** — Uses [nikic/php-parser](https://github.com/nikic/PHP-Parser) to extract structured column, index, and FK data from migration ASTs
- **SchemaIntrospector** — Queries actual database schema via INFORMATION\_SCHEMA
- **TypeMapper** — Bidirectional mapping between SQL types and Laravel Blueprint methods
- **MigrationGenerator** — Generates properly formatted migration files with `up()` and `down()` from schema diff actions
- **ConsolidationService** — Replays migration operations in order to produce a single equivalent migration per table
- **BackupService** — JSON snapshots of the migrations table with automatic rotation

---

⚠️ Limitations
--------------

[](#warning-limitations)

- **Schema comparison** requires `CREATE DATABASE` permission (gracefully skipped on SQLite or restricted permissions)
- **Consolidation** skips multi-table migrations to preserve safety
- **Type normalization** covers common MySQL/PostgreSQL/SQLite types — exotic custom types may need manual review
- **Column modifiers** (nullable, default) are detected in schema comparison but not fully replayed during consolidation
- **Partial analysis** — Migrations with raw SQL or conditional logic are flagged with warnings; schema checks cover only the parseable Blueprint parts

---

🧪 Testing
---------

[](#test_tube-testing)

```
# Run the full test suite (230 tests, 497 assertions)
vendor/bin/phpunit

# Static analysis (PHPStan level 6 with Larastan)
vendor/bin/phpstan analyse
```

Tested across:

- PHP 8.2, 8.3, 8.4, 8.5
- Laravel 11.x, 12.x

---

🙏 Acknowledgements
------------------

[](#pray-acknowledgements)

- [roslov/laravel-migration-checker](https://github.com/roslov/laravel-migration-checker) — inspiration for this package

---

📄 License
---------

[](#page_facing_up-license)

MIT License — see [LICENSE](LICENSE) file

---

**Made with 💙💛 for the Laravel community**

###  Health Score

41

—

FairBetter than 89% of packages

Maintenance89

Actively maintained with recent releases

Popularity18

Limited adoption so far

Community6

Small or concentrated contributor base

Maturity42

Maturing project, gaining track record

 Bus Factor1

Top contributor holds 100% of commits — single point of failure

How is this calculated?**Maintenance (25%)** — Last commit recency, latest release date, and issue-to-star ratio. Uses a 2-year decay window.

**Popularity (30%)** — Total and monthly downloads, GitHub stars, and forks. Logarithmic scaling prevents top-heavy scores.

**Community (15%)** — Contributors, dependents, forks, watchers, and maintainers. Measures real ecosystem engagement.

**Maturity (30%)** — Project age, version count, PHP version support, and release stability.

###  Release Activity

Cadence

Every ~2 days

Total

8

Last Release

57d ago

### Community

Maintainers

![](https://www.gravatar.com/avatar/980758347d99b5cdd75e45db912387679945eb12bb5e307e17cfdbdff31c5750?d=identicon)[redshoes](/maintainers/redshoes)

---

Top Contributors

[![erimeilis](https://avatars.githubusercontent.com/u/55956525?v=4)](https://github.com/erimeilis "erimeilis (16 commits)")

---

Tags

laravelmigrationslaravelschemamigrationssyncdrift

###  Code Quality

TestsPHPUnit

Static AnalysisPHPStan

### Embed Badge

![Health badge](/badges/erimeilis-laravel-migrations-drift/health.svg)

```
[![Health](https://phpackages.com/badges/erimeilis-laravel-migrations-drift/health.svg)](https://phpackages.com/packages/erimeilis-laravel-migrations-drift)
```

###  Alternatives

[barryvdh/laravel-ide-helper

Laravel IDE Helper, generates correct PHPDocs for all Facade classes, to improve auto-completion.

14.9k123.0M687](/packages/barryvdh-laravel-ide-helper)[tucker-eric/eloquentfilter

An Eloquent way to filter Eloquent Models

1.8k4.8M26](/packages/tucker-eric-eloquentfilter)[roots/acorn

Framework for Roots WordPress projects built with Laravel components.

9682.1M97](/packages/roots-acorn)[aedart/athenaeum

Athenaeum is a mono repository; a collection of various PHP packages

255.2k](/packages/aedart-athenaeum)[tehwave/laravel-achievements

Simple, elegant Achievements the Laravel way

7012.8k](/packages/tehwave-laravel-achievements)[toponepercent/baum

Baum is an implementation of the Nested Set pattern for Eloquent models.

3154.7k](/packages/toponepercent-baum)

PHPackages © 2026

[Directory](/)[Categories](/categories)[Trending](/trending)[Changelog](/changelog)[Analyze](/analyze)
