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

Phased Migrations Laravel Package

hassanejazpvt/phased-migrations

Run Laravel migrations in controlled phases across deployments to avoid downtime and race conditions. Split schema changes into stages, apply them incrementally, and keep every environment in sync with safe, repeatable database rollouts.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its config before writing any migrations:

composer require hassanejazpvt/phased-migrations
php artisan vendor:publish --tag=phased-migrations-config

The three things to read first, in order:

  1. The published config (config/phased-migrations.php) — this is the contract for the package. It defines the phase names, the order they run in, and where each phase’s migration files live. Everything else follows from it.
  2. The service provider (src/PhasedMigrationsServiceProvider.php) — tells you which Artisan commands are registered and whether the package wraps or replaces Laravel’s own Migrator. That determines whether php artisan migrate still behaves normally.
  3. The README and the tests/ directory — the 1.0 release notes are only a changelog link, so the test suite is the most reliable description of supported behaviour. Treat it as the spec for edge cases like rollback and migrate:fresh.

First use case to try: the classic expand/contract column rename. Add an expand-phase migration that adds display_name and backfills it, deploy, then a contract-phase migration that drops name. Run both phases locally against SQLite or a scratch database and confirm the order with php artisan migrate:status before you put it anywhere near a deploy pipeline. If that two-step works, the package is doing its job.

Implementation Patterns

Organise around phases, not timestamps. The core workflow is moving files out of the flat database/migrations directory into per-phase directories (or tagging them with a phase attribute, depending on how the config exposes it). Adopt that layout immediately, even for trivial migrations — retrofitting later means touching every file.

Map phases onto deploy stages. The pattern that pays off daily is binding each phase to a pipeline step:

  • Pre-deploy phase: additive, backwards-compatible changes (new columns nullable, new tables, new indexes) — safe to run while old code is still serving traffic.
  • Deploy: ship the application code that reads/writes the new shape.
  • Post-deploy phase: destructive changes (drop old columns, remove old indexes, tighten constraints) — only safe once no old code instances remain.

This is the expand/contract discipline; the package’s value is that it makes the boundary explicit and enforceable rather than a convention people forget.

Make phase execution explicit in CI. Don’t rely on a bare php artisan migrate in your deploy script — invoke each phase as its own command step so a failure in the post-deploy phase doesn’t silently leave you half-applied. Wire this into Envoyer/Forge deploy hooks, GitHub Actions, or your Kubernetes init container the same way you’d wire any other migration step.

Keep migrations idempotent where the phase allows it. Because phases may be re-run manually during an incident, backfill migrations should be safe to execute twice (use insertOrIgnore, updateOrInsert, or guard on column existence). This costs little and saves a lot at 2am.

Use it in tests. Point your test suite at a fresh database and run all phases in order in a base test case or RefreshDatabase equivalent. If the package doesn’t hook into RefreshDatabase automatically, add a small trait that runs the phased migrator once per suite.

Local development: run all phases in declaration order so your local schema matches production after a full deploy. Don’t try to simulate partial deploys locally — that’s what a staging environment with real sequential deploys is for.

Gotchas and Tips

  • Phase order is configuration order, not filename order. Your timestamps are no longer the source of truth once files are grouped or tagged. Review the phase ordering in config as carefully as you review migration code; a misordered phase list is a production incident waiting to happen.
  • Rollback semantics need verifying, not assuming. Confirm what migrate:rollback does when migrations from several phases are mixed in the same batch. Ideally you roll back one phase at a time, in reverse. Check this against the source or tests before you rely on it during an incident.
  • migrate:fresh and migrate:refresh may behave differently from the phased commands. Verify both paths early; the first one you reach for in local debugging is usually migrate:fresh.
  • Config caching: if you edit config/phased-migrations.php on a deployed environment, php artisan config:cache is required before the change takes effect. Add it to your deploy script alongside the migration step.
  • Multiple app servers / shared database. The phased approach assumes migrations run once per deploy, not once per instance. If your infrastructure runs deploy hooks on every node, guard the migration step with a lock or run it from a single designated host.
  • The 1.0 release notes carry no migration guidance. There’s no stated breaking-change list, no upgrade path, no changelog detail beyond a commits link. Since there is no prior assessment to compare against, treat this as a greenfield adoption: pin the version in composer.json ("^1.0"), read the source before upgrading across minor versions, and don’t assume semver discipline from a package that shipped 1.0 without release notes.
  • Extension points: the phase resolver and the migrator binding in the service provider are the two places to hook if you need custom phase logic (e.g., deriving a phase from an environment variable). Prefer binding a decorator in your own service provider over editing the package’s config in ways the tests don’t cover.
  • Watch for collisions with Laravel’s migrations table. If the package adds a phase column or a parallel tracking table, confirm how it records batches — this matters the first time you need to reconcile what actually ran in production.
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.
jinas/dhivatar
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource