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

Data Migration Laravel Package

fndmiranda/data-migration

A Laravel package for managing data migrations separately from schema migrations. It provides versioned data migration classes and Artisan commands to transform, move, backfill, and roll back application data safely during deployments and refactors.

View on GitHub
Deep Wiki
Context7

Getting Started

Install the package and publish its assets:

composer require fndmiranda/data-migration
php artisan vendor:publish --provider="Fndmiranda\DataMigration\DataMigrationServiceProvider"

Then run the package's own schema migration so it can track which data migrations have executed (it uses its own history table, separate from Laravel's migrations table).

Confirm the command surface, since names have shifted across versions:

php artisan list | grep data

You'll typically see something like data-migration:make, data-migration:run, data-migration:rollback, data-migration:status, and possibly data-migration:refresh.

First use case: reference / lookup data that must exist in every environment and evolve over time — roles, permissions, countries, currencies, default feature flags. These are the classic case where seeders don't fit (they're not tracked and get re-run) and schema migrations don't fit (you don't want data tied to Schema:: calls).

Generate your first one:

php artisan data-migration:make CreateDefaultRoles

That drops a file in the configured directory (commonly database/data-migrations/). Fill in up() to insert/update rows and down() to reverse them.

Where to look first:

  1. config/data-migration.php — path, tracking table name, connection.
  2. The generated file from make — the base class and available helpers.
  3. The tracking table row counts after a run — verify what actually executed.

Implementation Patterns

Reference-data sync with upserts. Write migrations as idempotent upserts keyed by a natural key (slug, code), not by auto-increment ID:

public function up()
{
    $this->updateOrCreate('roles', ['slug' => 'admin'],  ['name' => 'Administrator']);
    $this->updateOrCreate('roles', ['slug' => 'editor'], ['name' => 'Editor']);
}

public function down()
{
    $this->deleteWhere('roles', ['slug' => ['admin', 'editor']]);
}

Treat data migrations like schema migrations, not seeders. Append-only. Once a file has run in an environment, do not edit it — add a new migration. The tracking table records "this ran once," not "this data is still correct," so editing an executed file changes nothing in existing environments.

Environment-targeted data. Guard demo/dev rows so they never land in production:

if (app()->environment('local', 'staging')) {
    // sample customers, test products
}

For larger divergence, use per-environment subdirectories if the package supports path config; otherwise a single guard at the top of up().

Chunking large datasets. Wrap writes in DB::transaction() and chunk reads/writes so a crash doesn't leave partial state with no tracking record. For raw table syncs:

DB::transaction(function () {
    foreach (array_chunk($rows, 500) as $batch) {
        $this->insert('countries', $batch);
    }
});

Deployment pipeline. Order matters — schema first, data second:

php artisan migrate --force
php artisan data-migration:run --force

The --force flag is required in production because the command confirms interactively.

Rollback strategy. Implement down() with a deleteWhere keyed by the same identifiers used in up(). Prefer soft semantics (set a status column) over hard deletes for anything referenced by user data. Confirm whether data-migration:rollback rolls back one step or the last batch before relying on it.

Shared helpers via a base class. Extend the package's DataMigration base and add domain helpers (syncPermissions(), updateOrCreateByCode()), then have generated migrations extend yours. Keep those helpers idempotent.

Testing. In test setup, run the data migrations against the test DB and assert counts/values. If the tracking table pollutes tests, point the config's tracking table at an in-memory or refreshed table per test suite.

Gotchas and Tips

  • The tracking table is separate from migrations. Dropping it re-runs every data migration; back up or never truncate it outside local.
  • Idempotency is your responsibility. The package marks "ran," not "matched." If external systems mutate the data, add a re-sync command rather than reaching for re-running migrations.
  • Foreign key ordering. Data migrations run in filename/timestamp order; parent rows must exist before children. Split dependent data into clearly ordered files, or disable FK checks temporarily only inside a transaction.
  • Raw writes bypass Eloquent casts, mutators, and soft deletes. deleteWhere won't trigger deleted_at unless you implement it. JSON/array columns need explicit json_encode or the package's cast handling — verify.
  • Large migrations + tracking = partial-failure risk. The record is written after the run; a mid-run crash leaves data written but no history. Wrap in transactions where the DB supports DDL-safe transactions, and chunk.
  • --force in production. Missing it in CI silently fails the step if output isn't checked; also watch for prompts hanging non-interactive jobs.
  • Path/config drift. Changing the configured migrations directory after a run makes data-migration:status appear empty. Keep the config stable across environments.
  • Debugging. Use --pretend/dry-run if available; otherwise run against a clone. data-migration:status should be your first check — it tells you what the tracker thinks has run versus what's on disk.
  • Extension points. Publish and edit config; subclass the base migration for shared behavior; register custom commands if you need a data-migration:sync that re-applies upserts without touching history.
  • Small-package caution. At 35 stars, the docs and edge-case handling are thinner than Laravel's own tooling. Pin a version in composer.json, read composer.json for the supported Laravel range before upgrading, and be ready to read the source for exact helper signatures (updateOrCreate, deleteWhere, etc.) — they're the fastest way to resolve "method not found" surprises.
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.
snowsoft/faker
meditationsteps/blogetc
animalyoung/laravel-influxdb
onix-solutions/sms-telera
lonely-lights/prosetta
oxygen/data
oxygen/core
jengo/base
xiloryx/log-collector
a21ns1g4ts/filament-stripe
la-php/laravel-admin
artisan-build/community-prompts
vleroy/smart
ayles-software/laravel-australian-public-holidays
cboxdk/laravel-id-spatie
cboxdk/laravel-id-billing
cboxdk/laravel-id-billing-bridge
simple-as-fuck/php-performance-log
hsluv/hsluv
seampact/codegen