Reader Stacks

Aggregate Functions in Laravel Eloquent: count, sum, avg, min, max

Five aggregate methods cover almost every "how many" or "what's the total" question — the one thing worth understanding is that each one executes its own query and returns a scalar, not a collection.

Pulling every row into PHP just to count them, sum a column, or find a maximum value is unnecessary — Eloquent's aggregate methods push that calculation down to the database instead, and each one returns a single scalar value directly, not a Collection that then needs to be reduced in application code.

1. count()

$total = Order::count();
$pending = Order::where('status', 'pending')->count();

count() executes a SELECT COUNT(*) query — it doesn't fetch any rows at all, just the count, which is significantly cheaper than Order::all()->count() (which would pull every full row from the database only to count them in PHP afterward).

2. sum()

$totalRevenue = Order::where('status', 'completed')->sum('total');

3. avg()

$averageOrderValue = Order::avg('total');
// alias: Order::average('total');

4. min() and max()

$cheapest = Product::min('price');
$mostExpensive = Product::max('price');

5. Combining aggregates with other query constraints

$revenueThisMonth = Order::where('status', 'completed')
    ->whereMonth('created_at', now()->month)
    ->sum('total');

Every aggregate method respects whatever where clauses and other constraints are chained before it — the aggregate applies to the filtered result set, exactly as it would in a hand-written SQL query with a matching WHERE clause.

6. Aggregating within groups

$revenueByStatus = Order::select('status', DB::raw('SUM(total) as total_revenue'))
    ->groupBy('status')
    ->get();

foreach ($revenueByStatus as $row) {
    echo "{$row->status}: {$row->total_revenue}";
}

The dedicated aggregate methods (sum(), avg(), etc.) return one overall value across the whole query — computing a separate total per group instead needs a raw SELECT with GROUP BY, since there's no built-in "grouped sum" shorthand method.

7. Aggregating a relationship's count without loading it

$users = User::withCount('posts')->get();

foreach ($users as $user) {
    echo "{$user->name}: {$user->posts_count} posts";
}

withCount() adds a {relation}_count attribute to each model via a single efficient subquery, without loading the related records themselves — the right tool when only the count is needed (a "5 posts" badge next to a user's name), rather than eager-loading the full related collection just to call ->count() on it in a loop.

8. Aggregates via a relationship directly

$user->posts()->count();
$user->orders()->sum('total');

Calling an aggregate method directly on a relationship (rather than on the base model query) automatically scopes it to just that specific related model's records — no manual where('user_id', $user->id) needed, since the relationship definition already carries that constraint.

Topics: Database Queries & Eloquent