Difflock 1.0.0 is a Laravel package by Rati Rukhadze, available at `Heyosseus/difflock` on GitHub. It requires PHP 8.3 and supports Laravel 12 and 13. MySQL, MariaDB, PostgreSQL, and SQLite are supported.
php artisan difflock:lint
php artisan difflock:lint -v
php artisan difflock:lint --rule=drop-columnThe package reads migration source without loading or executing it. It combines that analysis with the live schema and table-size metadata to identify changes that need review before deployment. Its checks cover destructive operations, column changes, indexes, foreign keys, and sensitive column names.
`difflock:lint` examines pending migrations by default. In the supplied example, a migration adds a required `channel` column to `orders`, a constrained `customer_id` with `cascadeOnDelete()`, a nullable `card_number` with a length of 32, and an index on `status`. It also removes `legacy_token` from `customers` and renames `name` to `full_name`.
Against existing data, Difflock reports the required column, the `dropColumn()` operation, and the rename. It also warns that `cascadeOnDelete()` removes child rows inside the database, so model events, observers, and soft deletes do not take effect. The `change-column` rule compares `->change()` with the live column definition. `unindexed-foreign-key` checks whether the database engine supplies the required index. `redundant-index` detects a leading-prefix case such as `(status)` when `(status, created_at)` already exists.
The `-v` option expands findings, while `--rule=` limits the report to one rule. When no migration is pending, the command audits every migration file. Existing projects can accept their current backlog with `php artisan difflock:lint --all --accept`; the resulting baseline is stored in `database/difflock/accepted.json`.
Schema drift uses an observed database snapshot. Difflock does not need to reconstruct an expected schema from migrations, so conditionals, loops, and raw SQL in those files do not need interpretation. `php artisan difflock:diff --save` writes `database/difflock/schema.json`, which can be committed. A later `php artisan difflock:diff` compares the current connection with that file. The `--from=staging --to=production` form compares two configured connections.
The JSON baseline records tables, columns, indexes, defaults, and foreign keys. It contains no table rows or credentials. `snapshot.defaults`, `snapshot.comments`, and `ignore.tables` control what gets recorded. The article also points to Vacuum's schema linting for another PostgreSQL option.
Installing Difflock leaves `php artisan migrate` unchanged. Migration protection runs through `php artisan difflock:migrate`, which checks pending migrations and stops before database writes when findings reach the configured block level. `--allow-risky` bypasses this guard. Laravel's `--force` remains the separate confirmation flag for production migrations.
CI can run `php artisan difflock:check --ci`. The command returns 0 for a passing check, 1 for drift or findings at the configured threshold, and 2 when the check cannot run. Source-only rules can still run without a database connection, with missing table data reported.
`php artisan difflock:mcp` starts a standalone MCP server over stdio. Its four tools provide table context, migration linting, schema-drift checks, and rule documentation. The migration tool accepts a file path or source text, allowing an AI coding agent to inspect a migration before creating the file. `php artisan difflock:explain` produces a Markdown briefing without a language model or an external API.
Installation uses `composer require heyosseus/difflock --dev`. The package configuration can be published with `php artisan vendor:publish --tag=difflock-config`. `php artisan difflock:doctor` reports the connection, available tables, pending migrations, registered rules, and whether the configured database role can write. A read-only role is suitable for inspecting a production connection.




Comments
No comments yet — be the first.
Open the discussion
No account or password needed — just enter your e-mail and we’ll send you a one-time sign-in link. First time here? You’re set up automatically.
Your rating will be applied automatically after you sign in.
Check your inbox
We’ve sent a sign-in link to …. Open it on this device — this tab will sign you in automatically.
Nothing arrived? Check your spam folder — and mark the mail as "Not spam" so it lands in your inbox next time.