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 Core Laravel Package

jenssegers/mongodb-core

Core library powering Laravel MongoDB integration, providing the connection, query builder, and Eloquent model foundations used by jenssegers/mongodb. It enables MongoDB-backed models and fluent queries in Laravel while keeping shared internals separate for reuse and maintenance.

View on GitHub
Deep Wiki
Context7

Getting Started

Confirm what you're actually installing before wiring anything up. jenssegers/mongodb-core is the query-builder layer extracted from the main jenssegers/mongodb package — it gives you a Connection, Query, and Builder but no Eloquent model, no migration support, and no service provider conventions you'd expect from the full package. If you need Model::where(...)->get(), you want jenssegers/mongodb (now mongodb/laravel-mongodb), not this. Use this package only if you're building your own layer on top of it or pinning to an old Laravel that the full package no longer supports.

Minimal steps:

  1. composer require jenssegers/mongodb-core and verify the resolved version against your Laravel version (composer show jenssegers/mongodb-core). This package tracks old Laravel releases; a mismatch surfaces as a Class not found on Illuminate\Support\Manager or a broken ConnectionResolver.
  2. Ensure the mongodb PHP extension is installed and its version is compatible with the mongodb/mongodb library version the package requires. Mismatches here throw MongoDB\Driver\Exception\InvalidArgumentException at connection time, not at install time.
  3. Register a connection in config/database.php:
'connections' => [
    'mongodb' => [
        'driver'   => 'mongodb',
        'host'     => env('DB_HOST', '127.0.0.1'),
        'port'     => env('DB_PORT', 27017),
        'database' => env('DB_DATABASE'),
        'username' => env('DB_USERNAME'),
        'password' => env('DB_PASSWORD'),
        'options'  => ['database' => env('DB_AUTH_DATABASE')], // authSource
    ],
],
  1. Bootstrap the connection resolver yourself — this is the piece the full package's service provider normally does for you. Something like:
$manager = new Illuminate\Database\Manager($container);
$manager->addConnection(config('database.connections.mongodb'), 'mongodb');
$container->instance('db', $manager);

Where to look first: the Connection class and its collection() / query() factory methods, then Query\Builder. Those two files are the entire public surface you'll be living in. Everything else (aggregation helpers, raw expressions) is thin sugar over MongoDB\Collection methods.

First use case: read-only reporting is the safest first integration — point the builder at an existing collection and pull documents with projections, no writes, so you can validate driver auth and field mappings before touching write paths.

$users = DB::connection('mongodb')
    ->collection('users')
    ->select(['name', 'email', 'created_at'])
    ->where('active', true)
    ->limit(20)
    ->get();

Implementation Patterns

Query builder as the primary interface. Treat the builder as a fluent facade over MongoDB's find / findOneAndUpdate / aggregate. The API deliberately mirrors Eloquent (where, whereIn, orderBy, skip, take, first, pluck, count), which is great for porting existing SQL-shaped code but dangerous when the semantics diverge (see Gotchas).

Raw operators where the fluent API runs out. Anything the builder doesn't model — $regex with options, $elemMatch, $text search, $expr — goes through whereRaw:

->whereRaw([
    'tags' => ['$in' => ['php', 'laravel']],
    'score' => ['$gte' => 80, '$lt' => 95],
])

Keep the builder's structured where calls for the 80% case and whereRaw for the rest; mixing them is fine because the builder just merges into a single filter document.

Aggregation pipelines are where MongoDB earns its keep. Build them as explicit arrays and keep the stages in a named method rather than inline — pipelines get long fast and are the hardest thing to debug when a stage silently drops documents.

$pipeline = [
    ['$match'  => ['status' => 'paid']],
    ['$group'  => ['_id' => '$customer_id', 'total' => ['$sum' => '$amount']]],
    ['$sort'   => ['total' => -1]],
    ['$limit'  => 10],
];

$top = DB::connection('mongodb')->collection('orders')->aggregate($pipeline);

Writes. Use insert for single documents, insertGetId when you need the generated _id back, update with explicit operators when you care about atomicity:

->where('_id', $id)->update(['$set' => ['name' => $name], '$inc' => ['login_count' => 1]])

Always pass operators ($set, $unset, $inc, $push) — a bare array replacement is a footgun that deletes any field you omitted.

Upserts and atomic read-modify-write. update($ops, ['upsert' => true]) and findAndModify are your tools for counters and idempotent job queues. They're the main reason to skip Eloquent for a subsystem: you get real atomicity without transactions.

Integration tip: isolate by connection, not by model. Because this package has no Eloquent layer, define a small repository per collection and inject DB::connection('mongodb') into it. That keeps the query-builder dependency in one file per aggregate and makes it trivial to swap to the full package later if you need relations.

Testing. Point the test environment at a disposable database and truncate between tests with ->collection($c)->truncate() or drop(). There's no RefreshDatabase trait without Eloquent, so a small base TestCase helper that drops the database in tearDown is worth the ten lines.

Gotchas and Tips

_id is an ObjectId, not an integer. find(1) in Eloquent-speak does not work. You need new MongoDB\BSON\ObjectId($id) in where('_id', $oid), and route-model binding doesn't exist here. A missing cast is the single most common source of "returns null but the document is definitely there."

update() returns a result object, not a boolean. Code that does if (! $builder->update(...)) will misbehave — a zero-match update is not an error. Check getModifiedCount() / getMatchedCount() explicitly. Same for delete(), which returns a delete result.

count() and paginate() semantics differ from SQL. Counting is a full collection scan without an index on the filter; on large collections this is a slow endpoint. If the package exposes paginate, the total-count query is often the bottleneck — use cursor-based pagination (skip/take on a sorted, indexed field) or estimate from a $collStats aggregate.

Nested field paths are dot-strings, not nested arrays in where. where('address.city', 'Berlin') works; where('address', ['city' => 'Berlin']) performs a full-subdocument match and will miss documents that have extra fields in address. Use ->where('address.city', ...) or, when you truly want a subdocument match, whereRaw(['address' => ['$eq' => [...]]]).

Grouped closures map to $and, not SQL precedence. $builder->where(function ($q) { ... })->orWhere(...) produces a $or at the top level plus an $and inside the closure — verify the generated document before trusting nested conditions. There's a query-log equivalent (getQuery() / toMongo() on the builder) — print it early and often; it's the fastest way to catch precedence bugs.

Auth against admin. If the user was created in the admin database but the connection targets the app database, authentication fails with a misleading "not authorized" on the first read rather than at connect time. Set options.authSource (or options.database in older configs) explicitly.

Dates round-trip as MongoDB\BSON\UTCDateTime, not Carbon. The core package doesn't do the Eloquent date casting or Carbon conversion, so compare with new UTCDateTime($timestamp->getTimestamp() * 1000) and convert back manually in your hydration layer. Mixing PHP DateTime into a filter silently produces a string comparison and returns nothing.

Version pinning is the real risk. With no dependents and no recent maintenance signal, the package is effectively frozen — the mongodb/mongodb PHP library and the mongodb extension have moved on. Pin both in composer.json and add a CI job that runs the suite against the extension version your production image uses; drifting the extension under a pinned package is the most likely way this breaks in six months.

Extension points are thin. There's no query event / macro system comparable to Eloquent's Builder::macro. If you need cross-cutting behavior (soft deletes, timestamps, tenant scoping), wrap the builder in your own class or use whereRaw in a base repository. Don't fight the package to add behavior it never had.

Debugging checklist when a query returns nothing: dump the built document with getQuery(), then run the exact same document through mongosh against the same database. If the shell finds documents the builder doesn't, the problem is almost always ObjectId casting, a DateTime vs UTCDateTime mismatch, or a dot-path vs subdocument mismatch — in that order.

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.
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
johannesclimacus/laravel-tui
hocvt/seotools
vitalybaev/sypexgeo
amphibee/themosis-ignition
gito-me/fast-excel
frankyso/laravel-ipaymu
geolize-studio/laravel-chile
boomerangui/chat
cgv/localized-resource