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 Data Migrations Laravel Package

lacodix/laravel-data-migrations

Laravel Data Migrations adds versioned data migrations to Laravel apps, letting you ship seed and data changes alongside schema migrations with tracking and repeatable, idempotent execution.

View on GitHub
Deep Wiki
Context7

Getting Started

This is a maintenance handover release, so the first job is not learning a new API — it is swapping the package identity cleanly. The package itself still does the same thing: run "data migrations" (backfills, lookup-table seeds, reference data) through a separate migration track, with its own directory and its own history table, so they don't get tangled with schema migrations.

Swap the dependency, don't stack it. jlorente/laravel-data-migrations and lacodix/laravel-data-migrations must never be installed together — there is no replace directive, so Composer will happily let you end up with two packages registering overlapping commands and two classes with identical behaviour under different namespaces.

composer remove jlorente/laravel-data-migrations
composer require lacodix/laravel-data-migrations:^3.0
composer dump-autoload
php artisan optimize:clear   # config cache + compiled services/provider metadata

Check the platform matrix before you start. PHP 8.2 with Laravel 11 or 12, PHP 8.3 with Laravel 13. Laravel 10 and below are gone — if you're still on L10, stay on the upstream 2.x line and treat this as a scheduled upgrade, not a patch.

Then look at three things, in this order:

  1. php artisan list | grep -i data — confirm the command set is registered (make:data-migration, data-migrate, data-migrate:install, data-migrate:rollback, data-migrate:status, and friends). This release includes the upstream fix for Laravel 13.24+ where the package was clobbering Laravel's native migrate:install; if you saw that symptom, it's fixed here.
  2. Your published config (config/data-migrations.php or whatever your app published from 2.x) — verify the migrations path and the history table name. Republish and diff if you're unsure; a stale published config is the most common source of "it replayed everything".
  3. The existing history table — SELECT * FROM <history_table> ORDER BY batch — and confirm it lives where the new config says it does.

First use case: a lookup/reference data change that belongs in a deploy, not a seeder. make:data-migration AddCountryTaxRates, write an up() that upserts, run php artisan data-migrate, verify one row landed in the history table, then data-migrate:rollback to confirm down() actually works. Do that loop once on a throwaway migration before you trust it in a pipeline.

Implementation Patterns

Keep the two tracks ordered in your deploy script. Data migrations that touch a new column must run after the schema exists, and data migrations that backfill before a column is dropped must run before. The safe default ordering is:

php artisan migrate --force
php artisan data-migrate --force

If a release needs backfill-then-drop, split it across two deploys (expand/contract) rather than trying to interleave.

Treat up() as idempotent and down() as optional-but-explicit. Use upsert()/updateOrCreate() over blind insert() so a re-run after a partial failure doesn't blow up on a unique index. Where a rollback genuinely cannot restore deleted data, make down() throw with a clear message rather than silently no-op — a no-op down() that reports success is worse than a loud failure.

Backfill with chunkById, never get(). Data migrations run against production-sized tables. Chunk on the primary key, keep the batch size in config or a constant, and avoid ORDER BY on unindexed columns. For very large tables, consider a data migration that dispatches nothing and instead sets a "needs processing" flag, with a separate artisan command doing the heavy lifting — data migrations should finish inside a deploy window.

Use the query builder or DB::table(), not your Eloquent models. Models evolve: an observer added six months from now, a global scope, a $casts change, a renamed accessor — all of them can retroactively alter what an old data migration does. Raw table access pins the migration to the schema as it existed at write time. If you must use a model, wrap it in Model::withoutEvents() and accept that you've coupled the migration to that model's future.

Give data migrations their own connection if your app uses read/write splitting. Backfills are writes; routing them through a read replica config is a fun way to spend an afternoon. Set the connection explicitly in the package config, or force it at the top of up().

In tests, decide deliberately. RefreshDatabase does not run data migrations — it runs migrate. That's usually correct (tests should seed their own fixtures), but if your application logic depends on the reference data those migrations create, add an explicit $this->artisan('data-migrate') in the base test case setup, or move that reference data into a seeder and have the data migration call the same seeder. The second option is generally better: one source of truth, and tests get it for free.

Add a pending-migrations check to CI so drift is caught before deploy:

php artisan data-migrate:status   # fail the job if anything is "Pending"

Run that against a freshly migrated schema on every PR, after the namespace swap, as the cheapest possible regression test for the rename.

Gotchas and Tips

The namespace rename is the whole risk of this release. Jlorente\DataMigrations → Lacodix\LaravelDataMigrations. Grep for the old string across the entire repo, not just app/:

grep -rn "Jlorente" --include="*.php" --include="*.json" --include="*.xml" --include="*.neon" .

Places people forget: custom base migration classes that extend the package's migration class, bootstrap/providers.php / config/app.php provider entries, PHPStan/Psalm baseline files, PHPUnit bootstrap files, and any hand-rolled composer.json autoload mapping.

No automatic Composer replacement means transitive breakage. If another installed package depends on jlorente/laravel-data-migrations, Composer will either refuse to remove it or pull it back in. Check composer why jlorente/laravel-data-migrations before you start; if something depends on it, you're waiting on that package, not on this upgrade.

Verify the history table before the first run. The release notes call this out specifically: installations that never published an explicit config may have their history table in a location you didn't expect. If the new config points somewhere empty, the package concludes nothing has ever run and replays every data migration against production. Confirm the table exists and has your batch history, then run data-migrate:status — an empty "Pending" list is your green light.

Clear caches aggressively after the swap. config:clear for the published config, clear-compiled/optimize:clear for cached service-provider manifests, and composer dump-autoload for the classmap. On production with config:cache, a stale provider list produces the confusing failure mode where artisan commands run but the classes inside them don't exist.

Don't republish the config blindly. A 2.x config may have keys the 3.x default no longer uses, or vice versa. Publish to a temp path, diff against yours, merge by hand.

Transactions and DDL don't mix. MySQL implicitly commits on DDL, so wrapping a data migration in DB::transaction() while also doing Schema::table() gives you a false sense of atomicity. Keep DDL in schema migrations, keep DML in data migrations, and use transactions only around the DML.

Long deploys overlap with old code. During a rolling deploy, old app instances are still writing while your backfill runs. Anything that rewrites rows the old code also writes needs a second pass or a lazy "reconcile" command — a one-shot data migration will lose races.

Pin and watch. This is a single-maintainer fork on its first release (^3.0 is the right constraint), and note the explicit caveat that Laravel 11 compatibility isn't a security-support commitment. Subscribe to the repo's releases; if a patch appears fixing command registration for a Laravel point release, you want it quickly, because command-registration bugs tend to manifest as Laravel's own migrate:install behaving oddly rather than as an obvious package failure. When debugging anything migration-related, run php artisan about and check the registered commands list early — it's the fastest way to rule out the provider.

Extension point worth knowing: the package keeps its own history table and directory, so you can point it at a different connection or a different path per environment without touching schema migrations. If you're running multi-tenant setups, that per-tenant configurability is usually the cleanest integration path — but verify the config keys against the 3.x defaults rather than porting 2.x assumptions forward.

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