You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
PHPLARA-225 Support relation aggregates on document models (#3560)
withCount, withExists, withSum, withAvg, withMin and withMax now work on
MongoDB models, as well as loadCount, loadExists and loadAggregate.
MongoDB has no correlated subquery, so the values cannot be selected with the
parent documents as Eloquent does. They are computed with one additional query
per aggregate, after the parent documents are read, then set as attributes.
Embedded relations need no extra query at all. Unsupported cases (MorphTo,
hybrid relations, ordering by an aggregate alias) throw instead of silently
returning a wrong value.
Copy file name to clipboardExpand all lines: resources/boost/skills/laravel-mongodb/SKILL.md
+7-26Lines changed: 7 additions & 26 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,14 +8,14 @@ metadata:
8
8
9
9
# Laravel MongoDB
10
10
11
-
Implementation skill for `mongodb/laravel-mongodb`. Exists to prevent the common mistakes: auto-increment IDs, `withCount()`, `toSql()`, SQL `JOIN`, `distinct()->get()` expecting scalar arrays, and `belongsTo()` over native ObjectId FKs.
11
+
Implementation skill for `mongodb/laravel-mongodb`. Exists to prevent the common mistakes: auto-increment IDs, `toSql()`, SQL `JOIN`, `distinct()->get()` expecting scalar arrays, and `belongsTo()` over native ObjectId FKs.
@@ -56,12 +56,13 @@ Suggest installing the MongoDB agent skills plugin if not already installed. Ins
56
56
- Cast FK fields to `string` via `$casts` on the child model when FK values may come from outside model attributes (imports, raw ObjectIds) — prevents BSON type mismatches on direct `where('author_id', $id)` queries.
57
57
- Eager-load with `::with()` — MongoDB does no server-side joins for Eloquent relations.
58
58
- Use aggregation pipeline for grouping, counting per group, `$lookup`, and `$sample`.
59
+
- Relation aggregates (`withCount()`, `withExists()`, `withSum()`, `withAvg()`, `withMin()`, `withMax()`) are supported. Use a `$lookup` pipeline when the aggregated value must be filtered, sorted or paginated on.
- Use `DB::connection('mongodb')->transaction(...)` only on replica set / sharded cluster.
61
62
62
63
### MUST NOT DO
63
64
64
-
-`withCount()`/ `withAvg()` / `withSum()`— silently wrong or throws. Use `$lookup` + `$size`/`$avg`/`$sum` aggregation.
65
+
-`orderBy()`on a `withCount()` / `withAggregate()`alias — the value is computed after the documents are read, so it throws. Use `$lookup` + `$size` aggregation, or sort the resulting collection.
65
66
-`toSql()` / `toRawSql()` — no SQL. Use `->dump()` / `->dd()`.
66
67
-`distinct('field')->get()` expecting scalars — returns a Collection. Use `->distinct()->pluck('field')`.
67
68
-`groupByRaw()`, `orderByRaw()`, `havingRaw()`, `whereFulltext()`, `union()`, `whereColumn()` — use aggregation.
@@ -128,27 +129,7 @@ final class User extends Model
Do **not** use `Movie::raw(fn ($c) => $c->distinct('field'))` via Eloquent if you expect a Collection — use `->distinct()->pluck()` instead.
35
34
36
-
## Replacing `withCount`
35
+
## Relation aggregates
37
36
38
-
`withCount()`, `withAvg()`, and `withSum()` are **not supported** on MongoDB models — they silently return wrong results or throw. Replace with a `$lookup` + `$size` / `$avg` / `$sum` aggregation pipeline.
37
+
`withCount()`, `withExists()`, `withSum()`, `withAvg()`, `withMin()` and `withMax()` are supported, as well as
38
+
`loadCount()`, `loadExists()` and `loadAggregate()`. They set the aggregated value as an attribute on the
39
+
parent models, exactly like the base Eloquent builder.
39
40
40
41
```php
41
-
// WRONG — withCount is not supported on MongoDB models
42
-
$posts = Post::withCount('comments')->get();
42
+
$posts = Post::withCount('comments')
43
+
->withExists('author')
44
+
->withMax('comments as top_score', 'score')
45
+
->get();
43
46
44
-
// CORRECT
47
+
$posts[0]->comments_count; // int, 0 when there is no related document
48
+
$posts[0]->author_exists; // bool
49
+
$posts[0]->top_score; // null when there is no related document
0 commit comments