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

Mongodb Migrations Laravel Package

graviton/mongodb-migrations

MongoDB migrations for Laravel: run versioned, reversible migrations against MongoDB collections using familiar Artisan workflows, with migration tracking, rollback support, and team-friendly schema changes.

View on GitHub
Deep Wiki
Context7

Getting Started

Prerequisites, in order of failure likelihood. You need (1) the mongodb PHP extension, (2) a Laravel MongoDB driver layer — and this is the decision that dominates everything else: graviton/mongodb-migrations is old enough that it may assume jenssegers/mongodb rather than the current mongodb/laravel-mongodb, (3) a named connection in config/database.php under connections with 'driver' => 'mongodb', and (4) a Laravel/PHP version inside the package's constraints — which may be open-ended or absent entirely. Confirm the driver assumption in the source before writing any application code.

Install and pin. Because the package has no stars, no dependents, and no score, do not use a floating constraint. Install a specific tag or commit, and record why in the repo:

composer require graviton/mongodb-migrations:dev-master#<commit>
composer why-not graviton/mongodb-migrations <version>   # surface Laravel/PHP conflicts early

If the repository looks quiet or archived, prefer a VCS/path repository or a vendored fork over Packagist so you control the code you deploy.

Where to look first, in this order:

  1. php artisan list filtered for the package's command namespace — this is the only reliable inventory of the command surface, since documentation is sparse. Note the create/run/rollback/reset commands and their exact names before you document anything.
  2. composer.json — bounded vs. open constraints, and the require block for the driver.
  3. The service provider — whether it auto-discovers (extra.laravel.providers) or needs manual registration in config/app.php/bootstrap/providers.php, and what config it merges (connection name, migrations collection name).
  4. The migration repository class — this is the bookkeeping layer and the thing that determines whether rollback actually works. Find the collection it writes batches into.
  5. The migration stub(s) — the shape of what you'll actually author.

First use case: one index, end to end. Do not start with a backfill. Create a migration that adds a single-field index on a non-critical collection, run it, then open a mongosh session and verify two things: the index exists with the options you asked for, and a record appeared in the package's migrations collection. Then roll it back and re-apply. That loop — apply, verify in mongosh, rollback, re-apply — is the whole product; if any step is broken you've learned it in ten minutes instead of in production.

First-day deliverable: a short internal runbook covering apply, rollback, how to recover from a partially applied migration, and which connection the tool targets in each environment. Sparse documentation means you author this or nobody has it.

Implementation Patterns

Separate index migrations from data migrations. This is the single most valuable convention. Index migrations are fast, reversible, and idempotent-by-nature; data backfills are slow, resumable-with-difficulty, and often irreversible. Keeping them in distinct files means a failed backfill doesn't leave your index state unknown, and it lets you gate them differently in the deploy pipeline (indexes in the normal migrate step; backfills as a separate, manually-triggered job until proven).

Write indexes idempotently. A migration that throws "index already exists" on a re-run after a partial failure is a migration you have to repair by hand at 2 a.m. Check listIndexes first, or wrap creation in a way that tolerates the existing-index error:

public function up(): void
{
    $collection = DB::connection('mongodb')->getCollection('orders');

    foreach ($collection->listIndexes() as $index) {
        if ($index->getName() === 'orders_tenant_status_created_at') {
            return; // already converged
        }
    }

    $collection->createIndex(
        ['tenant_id' => 1, 'status' => 1, 'created_at' => -1],
        ['name' => 'orders_tenant_status_created_at']
    );
}

public function down(): void
{
    DB::connection('mongodb')->getCollection('orders')
        ->dropIndex('orders_tenant_status_created_at');
}

Make down() real even if you never run it. Treat it as executable documentation of intent, and as the emergency lever during an incident. For an index it's a dropIndex. For a data transform it may legitimately be a no-op with a comment explaining why — but write the comment, don't leave it blank silently.

Batch and checkpoint backfills. Never iterate a whole collection in one pass. Page by _id and record your progress so a killed process can resume rather than restart:

$lastId = null;
do {
    $query = $lastId ? ['_id' => ['$gt' => $lastId]] : [];
    $batch = $collection->find($query, ['limit' => 1000, 'sort' => ['_id' => 1]]);
    $count = 0;

    foreach ($batch as $doc) {
        $collection->updateOne(['_id' => $doc['_id']], ['$set' => ['schema_v' => 2]]);
        $lastId  = $doc['_id'];
        $count++;
    }
} while ($count > 0);

Prefer a version field (schema_v) on documents over a separate tracking document — it makes the migration idempotent and lets you query how far convergence has actually progressed.

Use a migration-per-intent naming scheme with ordered prefixes, matching Laravel's timestamp convention so cross-store ordering stays readable in a shared deploy log. If the tool's generator uses its own prefix, keep it — don't invent a second scheme.

Wire into the deploy as a discrete step, not a side effect. The realistic pipeline shape:

php artisan migrate --force          # relational
php artisan <package>:migrate --force # mongodb

Run the Mongo step in an init container or a single designated runner, not from every application replica — the package's most likely missing feature is a distributed lock, so concurrency control is your job. Add a --dry-run/--pretend equivalent if one exists; if it doesn't, generate the migration's operations by running against a fresh local database first.

CI: assert convergence, not just success. A migration that "succeeded" while creating the wrong index options is worse than a failure. Boot an ephemeral MongoDB (container service in CI), apply all migrations, then diff listIndexes output against an expected fixture. Run apply twice in CI to prove idempotency, and run rollback + re-apply on a throwaway database to prove reversibility. This is the cheapest insurance you can buy against a quiet upstream.

Guard the environment. Make the tool read its connection from a single config key so staging and production cannot diverge by accident, and add an explicit guard — refuse to run data migrations when app()->environment('production') unless a flag is set. Mongo migrations have no transactional DDL safety net; the guard is your only undo.

Extension points to lean on. The migration repository and the stubs are the two places worth customizing: a stub that pre-fills the collection name and index name convention saves every future migration from drift, and a repository subclass is where you'd add a lock or a dry-run if you end up hardening the package.

Gotchas and Tips

The driver abstraction is the trap door. If the package targets jenssegers/mongodb and you're on mongodb/laravel-mongodb, connection and collection access differ and you'll see class-not-found or method-not-found errors deep inside the migration runner, not at install time. Grep the source for Jenssegers, MongoDB\Laravel, and raw MongoDB\Driver\Manager usage before anything else. This single detail can consume more time than the rest of the integration combined.

No lock means racing replicas. Two containers starting simultaneously can both see a migration as pending, both run it, and one can record a success it didn't earn. Symptoms are intermittent, environment-specific, and awful to debug. Mitigate structurally: single-runner execution, an advisory lock in a separate tool, or a findOneAndUpdate-based mutex in your deploy script. Do not rely on the package for this.

Index builds on large collections hurt. A foreground build on a big collection can spike write latency or stall writes outright. Modern MongoDB (4.2+) no longer accepts background: true for the standard build path, and rolling builds are a manual procedure — check whether the package exposes any knob at all. The safe pattern is: build on secondaries / during a maintenance window, on a collection whose size you've actually measured, with a rollback plan that is a literal dropIndex.

A dropped tracking collection = silent divergence. If someone restores a database snapshot without the migrations collection, the tool believes nothing has been applied and will attempt everything again. Back up the tracking collection alongside your data, and know its name so you can inspect it during an incident. db.<migrations_collection>.find().sort({_id:-1}) should be in your runbook.

Partial application leaves no record. Expect a migration that fails midway to have already created one index and not recorded the batch — so a naive re-run errors on the existing index. The idempotent-up() pattern above is the mitigation; adopt it from migration one rather than retrofitting it after the first incident.

Rollback maturity is unknown until tested. rollback and reset may be partial or missing entirely. Test them explicitly in the sandbox on a disposable database before you depend on them. If they don't work, your incident procedure is "write and run the dropIndex by hand," and that needs to be stated in the runbook rather than assumed away.

Missing --pretend is a real cost. Without a dry-run you cannot review the exact operations before they execute. Substitute a local database: apply the migration against a throwaway instance, capture what changed via listIndexes / document counts, and put that diff in the pull request. Reviews of migrations are the whole point of the tool; don't let a missing flag erode them.

Empty collections behave differently. Some operations only bite when a collection exists but is empty, or when a collection has never been created (Mongo creates implicitly on first write). Test migrations against a fresh database, not just your seeded local one — that's the state staging and production will be in.

Advanced index options need raw arrays. TTL (expireAfterSeconds), partial filter expressions, collations, and JSON schema validators are where a thin wrapper usually falls short. If the fluent helper won't express it, drop to the driver's createIndex/createCollection with a raw options array rather than contorting the API — but note in the migration comment that you bypassed the wrapper so the next person isn't surprised.

Test isolation. Point tests at a database name suffixed per run and drop it in teardown; a shared test database plus migrations is a recipe for order-dependent flakes. If the tool has no transaction story (it won't — Mongo transactions don't cover DDL here), isolation is entirely on you.

Budget for ownership. With zero stars and zero dependents, assume no upstream fix will arrive for a Laravel major upgrade or a PHP/driver bump. Keep the package behind your own thin wrapper class (IndexManager or similar) so that replacing it with a few hundred lines around listIndexes/createIndex/dropIndex is a swap, not a rewrite. That wrapper is also where you'd add the lock and dry-run the package lacks.

Debugging checklist, in order: does php artisan list show the commands at all (provider not registered)? Does the command report the expected connection name (config not merged)? Did a record land in the migrations collection (repository broken or writing elsewhere)? Do listIndexes in mongosh match the migration source (silent partial success)? Answer those four before reading driver internals.

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.
uni-method/symfony-lib
tacticmedia/scim-server-bundle
neika-ai/symfony-bundle
mattleads/agent-publishing-bundle
glavweb/framework-bundle
glavweb/extra-bundle
glavweb/api-bundle
glavweb/admin-bundle
adachsoft/api-version-bundle
elsayed85/laravel-api-exceptions
ouhssini/laravel-demo-mode
daworks/ncloud-outbound-mailer-driver
gp247/cms
ignibyte/aic-gates
nexa-merchant/blog
novasa/laravel-language-center
opencage/geocode
adolfoholzer/parameters
livenetworks/ln-starter
shitoudev/permissionmanager