aptcreativedesign/mongodb-laravel-livewire-tables
Integrates MongoDB with Livewire-powered tables in Laravel, letting you query, sort, and paginate MongoDB collections through a Livewire table component. Suited to Laravel apps already using MongoDB that want Livewire table UI without extra glue code.
Install the full dependency chain explicitly. The package is a bridge, not a standalone component — it adds MongoDB query support on top of Rappasoft's Laravel Livewire Tables. Install all four together and pin versions in composer.json:
composer require aptcreativedesign/mongodb-laravel-livewire-tables \
rappasoft/laravel-livewire-tables \
livewire/livewire \
mongodb/laravel-mongodb
Don't assume constraints from older tutorials still apply — the version history jumps from v1.02 to v4.0.0, so the package has almost certainly moved to Livewire 3 / Laravel 10+ APIs.
Read the source, not the docs. There is no documentation beyond the README and a release note containing only a compare link. Read in this order: (1) composer.json for the actual version constraints, (2) the src/ base component and any query/search/sort traits, (3) the diff at compare/v1.02...v4.0.0 — that diff is the migration guide.
Wire up the connection before touching the component. Add a mongodb connection in config/database.php, install the mongodb/laravel-mongodb service provider, and set $connection = 'mongodb' on the model that will back the table. The table component is only Mongo-aware if the underlying model resolves to a Mongo connection — a misconfigured model silently produces empty results rather than an error.
First use case: an append-only log or activity feed. Point builder() at a Mongo-backed Activity model, expose a handful of string columns, and confirm global search + sorting + pagination on a small collection (a few hundred documents). This surfaces ObjectId-as-primary-key issues and regex search quirks cheaply, before you build a real admin screen.
Smoke test the query document in Tinker. Mongo has no SQL, so toSql() won't help. Dump the builder and inspect the filter/sort document it produces:
$q = Activity::query()->where('level', 'error');
dump($q->getQuery()->wheres ?? $q->toMongo()); // inspect the actual filter document
Enable query logging via the MongoDB driver's debug options early — it's your primary debugging tool for the rest of the project.
Subclass the component rather than monkey-patching. Extend the package's base table (or use its trait) so your tables inherit the Mongo-aware search/sort/query handling while you keep your own columns(), filters(), and row rendering:
class ActivityTable extends MongoTableComponent
{
public function builder(): Builder
{
return Activity::query()->where('tenant_id', auth()->user()->tenant_id);
}
public function columns(): array
{
return [
Column::make('Level')->sortable()->searchable(),
Column::make('Message')->searchable(),
Column::make('Created At')->sortable(),
];
}
}
Treat builder() as a Mongo query builder, not a SQL one. Prefer Model::query() with where, whereIn, whereBetween. Anything emitting SQL text — whereRaw, having, whereHas across SQL-style joins — will not translate. For related data use $lookup in an aggregation pipeline.
Search columns deliberately. Searchable columns on Mongo are typically translated to case-insensitive regex. Restrict searchable() to string fields; numeric, boolean, and array fields can produce type-mismatch matches or throw. For large collections, add a text index and prefer $text search over per-field regex.
Always add a stable tiebreaker to sorts. Mongo has no guaranteed natural order, so ORDER BY created_at DESC alone can shuffle rows between pages. Pair every sort with _id or a timestamp to keep pagination deterministic.
Use aggregation pipelines for computed and nested fields. Rather than selectRaw, build $project/$addFields stages and feed the resulting cursor to the table. This is also the clean way to flatten embedded document fields (address.city) into flat columns.
public function builder(): Builder
{
return Activity::query()->raw(function ($collection) {
return $collection->aggregate([
['$match' => ['level' => 'error']],
['$addFields' => ['city' => '$address.city']],
['$sort' => ['created_at' => -1, '_id' => -1]],
]);
});
}
Write filters as closures returning the builder. That way the UI filter and any programmatic re-query share one code path. Map UI semantics to BSON operators explicitly: date range → $gte/$lte on BSON dates, multi-select → $in, toggle → boolean.
Route exports and bulk actions through the same builder. If CSV export or bulk delete calls Model::all() independently, the user's active filters silently don't apply — a classic bug report. Expose a single filteredQuery() method used by both the table and the actions.
Version-guard the upgrade. Because this is a 1.x → 4.x jump, do the upgrade in a branch, run the table test suite against both old and new Laravel/Livewire constraints, and pin exact versions until the new behavior is confirmed.
The release notes are effectively empty. The v4.0.0 note contains only a changelog URL — no migration guide, no breaking-change list. Treat the major bump as breaking by default and read the compare/v1.02...v4.0.0 diff yourself before upgrading any production table.
_id is not id. ObjectId primary keys break assumptions in several places at once: row keys, wire:key stability, route model binding in row actions, and any where('id', ...) copied from SQL habits. Cast or expose an explicit string id and use it consistently.
Mixed and missing fields sort unpredictably. Mongo sorts across BSON type boundaries by type order, so a field that's sometimes a string, sometimes a number, sometimes absent will interleave strangely. Normalise on write, or add a $match stage to exclude documents missing the field.
Regex search is not LIKE. User input containing ., *, (, or \ becomes regex metacharacters and can throw or wildly over-match. Escape input (preg_quote) before constructing the regex, and consider $text search on indexed fields for large collections.
Dates are BSON, not strings. Carbon casts and whereDate do not behave as they do on MySQL. Use whereBetween with Carbon instances and verify the column is cast to datetime on the model — otherwise comparison silently matches nothing.
Pipeline + pagination is the classic failure point. skip/limit on an aggregation without a deterministic $sort will duplicate or drop rows across pages. Sort before you slice, and include _id in the sort key.
Debugging workflow. Enable query logging in mongodb/laravel-mongodb (or dump the raw command via the driver's debug mode), then dump the filter document before execution. When a table renders empty, the cause is almost always a type mismatch (string "5" vs int 5, or a null vs missing field) rather than the component itself.
Extension points. The package's real value is its overridable search/sort/query hooks. If a feature is missing, override the method in your subclass rather than forking — the next major bump will stay mergeable.
Upgrade checklist. Bump in a branch, run the table test suite, diff src/ against the previous tag, and check for renamed or dropped method signatures on anything you've overridden. Given the 1.x → 4.x distance, expect renamed methods and removed Livewire 2-era APIs.
How can I help you explore Laravel packages today?