Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Laravel Migration Compatibility Laravel Package

konekt/laravel-migration-compatibility

A Laravel package for checking database migration compatibility, helping you catch potential issues before upgrading Laravel or deploying schema changes. Useful for keeping migrations safe and portable across environments.

View on GitHub
Deep Wiki
Context7

Getting Started

This package exists to solve one specific migration headache: foreign key column type mismatches (MySQL error 3780 — "Referencing column and referenced column in foreign key constraint are incompatible"). Laravel has moved from unsignedInteger() (int) to unsignedBigInteger()/foreignId() (bigint) over the years, so when you add a foreign key to a table you didn't create — a package migration running against a host app, or a legacy table with an old int PK — you need to know whether the referenced id is int or bigint before you write the column.

Minimal setup:

  1. composer require konekt/laravel-migration-compatibility in the consuming app or package.
  2. There is no meaningful service provider to publish — the package is a thin helper around schema introspection.
  3. Look first at the package's src/ for its single compatibility helper class, and check its README's usage snippet for the exact method name (the API surface is intentionally tiny).
  4. First use case: a migration that creates xxx_id referencing an existing table whose migration ran earlier and whose PK type you can't assume:
// pseudocode — confirm exact names in the package's README
$type = MigrationCompatibility::getPrimaryKeyType('users'); // 'int' | 'bigint'

$table->{$type === 'bigint' ? 'unsignedBigInteger' : 'unsignedInteger'}('user_id');
$table->foreign('user_id')->references('id')->on('users');

If you're only ever writing migrations against tables you also create, and you've fully moved to Laravel 8+, you don't need this package — foreignIdFor() or foreignId() is enough. The value shows up exclusively at the "package vs. host app" and "legacy vs. new" boundary.

Implementation Patterns

Guard the column definition, not the constraint. The mismatch error comes from the column type, so the branch belongs on the column definition. Wrap the integer/bigInteger decision, then let $table->foreign(...) be unconditional. Keeps the migration readable.

Wrap it in a small helper method on the migration. Rather than sprinkling introspection calls through every migration, define a private method once per migration class (or a shared base migration) like idColumnFor($table), and call it wherever you need the right column type. This keeps the package reference in one place if you ever swap it out.

Use it in package migrations, not app migrations. App code knows its own schema. The real pattern is a distributable package (module, plugin, SaaS addon) whose migration adds user_id to orders — it doesn't own users, so it must introspect. This is exactly the "opportunity" niche the package targets.

Pair it with Schema::hasTable() / Schema::hasColumn(). The package tells you the type; Laravel's own Schema facade tells you whether you're even dealing with the right table. Combine both for defensive migrations that run cleanly across Laravel versions:

if (Schema::hasTable('users') && !Schema::hasColumn('orders', 'user_id')) {
    // add column using detected type
}

Use it as a fallback, not a primary path. In your own migrations, write the explicit type. Only fall back to introspection when the migration must run against unknown foreign schemas. Overusing the helper makes migrations slow and fragile (see gotchas).

Test matrix pattern. If you maintain a package, run its migrations in CI against both a fresh Laravel app (bigint) and a legacy fixture schema (int). This is the only way to catch that your helper actually branches correctly.

Gotchas and Tips

Schema introspection hits the database at migration time — not at compile time. That means the migration now depends on connection state, which connection is active, and whether the referenced table already has rows/columns in this environment. In a fresh install where users hasn't been migrated yet, or on SQLite, introspection can return nothing useful — always have a sensible default (usually unsignedBigInteger).

SQLite and in-memory testing. SQLite's type affinity is loose and Laravel's schema builder maps everything through it; introspection behavior differs from MySQL/Postgres. If you test with :memory:, expect the helper to behave differently than production. Test the helper against the DB engine you actually deploy.

Migrations that read schema must be strictly ordered. Because the helper checks the current DB state, the referenced table's migration must have already run in the same batch sequence. If you reorder or squash migrations, or run a subset, you can get a null/garbage result. Prefer deterministic explicit types where possible.

Multi-tenant / multi-database setups are a trap. If your app switches connections per-tenant (stancl/tenancy, spatie/multitenancy), introspection reads whichever connection is active when the migration runs. Ensure the helper is called against the correct connection — you may need to explicitly set the connection before introspecting.

unsigned matters as much as int vs. bigint. MySQL requires the signedness to match too. Some apps use $table->integer('id', true) (signed) or unsignedBigInteger. Introspect and mirror both width and signedness; don't assume unsigned.

Prefer built-in Laravel where possible. $table->foreignId('user_id')->constrained() and foreignIdFor(User::class) handle the vast majority of cases from Laravel 8 onward — including bigint, unsigned, and constraint naming. Reach for this package only when you genuinely can't reference the model/table at migration-write time.

Down migrations should drop unconditionally. Don't introspect on the way down — just dropForeign and dropColumn. Introspection during rollback adds failure modes for no benefit.

Extension point is thin by design. Because the package is a single helper, the realistic extension is a base migration class or a trait in your own codebase that wraps it with your defaults (connection handling, fallback types, logging). Don't fork the package for this — wrap it.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus
elmogy/larafast