Model::withAggregate()
Load aggregated values from related models as attributes — count, sum, min, max, avg — in a single query without loading the relation.
Overview
withAggregate() is the engine behind withCount(), withSum(), withAvg(), withMin(), and withMax(). It adds a subquery-based aggregate as a virtual attribute to your models, avoiding the need to load entire relationships just to compute a number.
Usage
The shorthand helpers cover most cases:
$authors = Author::query()
->withCount('books')
->withAvg('books', 'rating')
->withSum('books', 'pages')
->withMax('books', 'published_at')
->get();
$authors->first()->books_count; // 12
$authors->first()->books_avg_rating; // 4.2
$authors->first()->books_sum_pages; // 3847
$authors->first()->books_max_published_at; // '2024-03-15'
For custom aggregates, use withAggregate() directly:
$users = User::query()
->withAggregate('orders', 'total', 'sum')
->withAggregate('orders as latest_order', 'created_at', 'max')
->get();
$users->first()->orders_sum_total; // 15430
$users->first()->latest_order; // '2024-06-01 14:32:00'
Constrained Aggregates
Add conditions to the aggregate subquery:
$users = User::query()
->withCount(['orders' => function ($query) {
$query->where('status', 'completed');
}])
->withSum(['orders as refunded_total' => function ($query) {
$query->where('status', 'refunded');
}], 'total')
->get();
Sorting by Aggregate
Order by the aggregated value directly:
$categories = Category::query()
->withCount('products')
->orderByDesc('products_count')
->get();
When to Use
- Displaying counts, totals, or averages alongside parent models
- Sorting or filtering by related model aggregates
- Dashboard queries that need stats without loading full relationships
- Replacing manual
DB::raw()subqueries with clean Eloquent syntax