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

Eloquent Mongodb Repository Laravel Package

nilportugues/eloquent-mongodb-repository

A repository layer for Eloquent MongoDB models in Laravel, wrapping query logic behind a clean, testable interface. Handles CRUD, filtering, and pagination so your models stay lean and data access stays consistent.

View on GitHub
Deep Wiki
Context7

Getting Started

Check compatibility before you composer require. This package was last released in March 2016 and pins the Laravel 5.x era of illuminate/* plus jenssegers/mongodb (since renamed mongodb/laravel-mongodb). On a modern stack the install will simply fail with a constraint conflict — in that case treat the repo as a reference implementation to read on GitHub rather than a dependency to install.

On a compatible (PHP 7.0/7.1, Laravel 5.4–5.6) stack:

composer require nilportugues/eloquent-mongodb-repository

You also need ext-mongodb (the new extension, not the legacy ext-mongo) and a connection in config/database.php:

'mongodb' => [
    'driver'   => 'mongodb',
    'host'     => env('DB_HOST', '127.0.0.1'),
    'port'     => 27017,
    'database' => env('DB_DATABASE'),
],

Where to look first. The package is a thin adapter — two or three classes under vendor/nilportugues/eloquent-mongodb-repository/src. Read those first, then the contracts it implements in vendor/nilportugues/repository/src/.../Contracts (Repository, Filter, FilterCollection, Sort, Order, Page, Pageable, Fields). Those DTOs are the real product; the Mongo class is just translation.

First use case. A read-side endpoint: fetch by id and return a filtered, sorted, paginated list.

// 1. Eloquent model (jenssegers/mongodb)
use Jenssegers\Mongodb\Eloquent\Model;

class User extends Model
{
    protected $connection = 'mongodb';
    protected $collection = 'users';
    protected $fillable   = ['name', 'email'];
}

// 2. Repository — set the model and the Mongo primary key
use NilPortugues\Foundation\Infrastructure\Model\Repository\EloquentMongoDB\EloquentRepository;

class UserRepository extends EloquentRepository
{
    protected $model      = User::class;
    protected $primaryKey = '_id';   // not "id"
}

// 3. Bind it
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);

Confirm the base class name and constructor signature against vendor/ — the 2016 releases differ slightly on whether the model is injected or resolved from the $model class string.

Implementation Patterns

Interface in the domain, adapter in infrastructure. Declare App\Domain\User\UserRepository yourself (or use the package's Repository contract), and bind the Mongo implementation in a service provider. That is the only reason to accept this dependency: your application code sees Filter/Sort/Pageable value objects, not Eloquent or BSON.

Translate HTTP into DTOs once. Put the query-string parsing in one factory per resource, not in controllers:

// ?name=jo&sort=-created_at&page=2&per_page=25
$filter   = new Filter(new FilterCollection([
    new Filter('name', new Like('jo')),
]));
$sort     = new Sort('created_at', new Order('DESC'));
$pageable = new Pageable(new Page(2, 25), $sort);

$page = $this->users->findAll($pageable);   // returns a Page DTO

Exact constructor shapes moved between releases — read the contracts package once and wrap construction in your own helpers so upgrades touch one file.

Keep custom queries out of the generic contract. Mongo-specific needs (regex search, $in batches, aggregation) belong in extra methods on the concrete repository or a narrower interface:

public function activeSince(DateTimeInterface $date): array
{
    return $this->model->newQuery()
        ->where('status', 'active')
        ->where('created_at', '>', $date)
        ->get()->all();
}

public function statsByPlan(): array
{
    return $this->model->raw(fn ($collection) => $collection->aggregate([
        ['$group' => ['_id' => '$plan', 'count' => ['$sum' => 1]]],
    ])->toArray());
}

Map documents to domain entities at the boundary. Don't let Jenssegers\Mongodb\Eloquent\Model leak into services. A small UserMapper::toEntity(array $doc) / toDocument(User $entity) pair keeps save() and find() clean and makes swapping in an in-memory repository trivial.

Test in two layers. Unit-test services against a hand-written in-memory implementation of the same interface (the nilportugues/repository contracts are what make this possible). Integration-test the Mongo adapter against a real MongoDB container — there is no SQLite equivalent here, so mocks will not catch operator translation bugs.

Run both backends if you're migrating. Because the sibling SQL repository shares the contract set, the same service code can be bound to either implementation per environment. That is the package's strongest selling point and the only sane way to justify it.

Gotchas and Tips

  • It is effectively abandoned. Last release 2016-03-19, 18 stars, zero dependents. There is no community to answer questions, no PHP 8 release, and no security patching. Fork and vendor it into your repo, pin an exact version, and commit composer.lock. Do not let it float.
  • _id is not id. If $primaryKey stays id, save() will not match the existing document and you will silently insert duplicates. find(Id $id) also needs the raw string form of the ObjectId — cast $model->getKey() to string before wrapping it, and cast incoming request ids to ObjectId explicitly.
  • Filter operators were written with SQL semantics. LIKE may compile to %term% in ways MongoDB cannot express, and BETWEEN/IN/NIN need $gte/$lte/$in. Write a test per operator (eq, neq, gt, gte, lt, lte, in, nin, like, between) against known documents before trusting a single filter in production.
  • Unanchored regex means collection scans. Case-insensitive search (/term/i) cannot use an index. Normalize fields into lowercase search columns or use Atlas Search/text indexes if the collection grows.
  • Sorting is BSON-type ordered and unstable on missing fields. Mixed stored types (string vs int, string vs BSON date) sort in the BSON type order, which surprises everyone coming from SQL. Documents without the field sort first.
  • Pagination counts twice. A Page needs a total, so findAll() runs the query plus a count(). On large collections that count is the expensive part — cache it, or override the total with estimatedDocumentCount().
  • No transactions. The package predates MongoDB 4.0 multi-document transactions. Anything that assumes a unit of work spanning collections is a lie; design around single-document atomicity.
  • $hidden is serialization, not projection. The base class' $hidden filters array output; it does not restrict fields fetched. Use Fields in the contract or ->project([...]) on the query for real projections.
  • Date filtering fails silently. If dates were stored as strings rather than BSON dates, where('created_at', '>', $carbon) matches nothing. Decide the storage type on day one and enforce it with a cast.
  • Debugging: enable the query log on the connection (->enableQueryLog() / ->getQueryLog()), inspect the built query with ->toMongo(), and verify the raw document with DB::connection('mongodb')->getCollection('users')->findOne([...]). Most "the repository is broken" bugs turn out to be a type mismatch visible only in the raw document.
  • Extension points are limited to subclassing: override findBy(), findAll(), count(), or save() on your concrete repository, and supply a field-mapping class if your entity property names differ from document keys. Wrap save() to fire domain events or validate — the base does neither.
  • Composer friction: if a transitive package drags in a newer illuminate/*, Composer's solver will either fail or silently downgrade your framework. Run composer why-not nilportugues/eloquent-mongodb-repository before you commit to it, and be prepared for the honest verdict: on a new project, use mongodb/laravel-mongodb directly behind your own repository interface and copy the Filter/Sort/Pageable design — not the package — into your codebase.
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
cgv/localized-resource