PHP

Efficient Aggregation of Related Models with Eloquent `withCount` and `withSum`

Optimize performance by retrieving aggregate values (like counts or sums) for related models without loading all relationships, directly within your main query.

use App\Models\User;
use App\Models\Post;
use App\Models\Order;

// Example 1: Get user with their posts count
// Assume a User hasMany Posts
$usersWithPostCounts = User::withCount('posts')->get();

echo "Users with Post Counts:
";
foreach ($usersWithPostCounts as $user) {
    echo "User: {$user->name}, Posts Count: {$user->posts_count}
";
}

// Example 2: Get posts with comments count and also total 'likes' (if comments had a 'likes' column)
// For demonstration, let's assume Post hasMany Comments and Comment has a 'rating' integer column
// and we want to sum the ratings of approved comments.
// (This requires a 'rating' column on comments table in real scenario)
$postsWithAggregations = Post::withCount('comments')
    ->withSum(['comments as approved_comments_total_rating' => function ($query) {
        $query->where('approved', true);
    }], 'rating')
    ->get();

echo "
Posts with Comments Count and Approved Comments Total Rating:
";
foreach ($postsWithAggregations as $post) {
    echo "Post: {$post->title}
";
    echo "  - Total Comments: {$post->comments_count}
";
    echo "  - Approved Comments Total Rating: {$post->approved_comments_total_rating}
";
}

// Example 3: Count users with at least one published post
// This uses a conditional aggregation within the main query, but applies to the parent.
// Let's assume a User hasMany Orders, and Order has a 'amount' column.
// Get users with total amount of 'completed' orders.
$usersWithCompletedOrderAmounts = User::withSum(['orders as completed_orders_amount' => function ($query) {
    $query->where('status', 'completed');
}], 'amount')->get();

echo "
Users with Total Completed Order Amounts:
";
foreach ($usersWithCompletedOrderAmounts as $user) {
    echo "User: {$user->name}, Completed Orders Amount: {$user->completed_orders_amount}
";
}

// Database migration examples:
// Schema::create('users', function (Blueprint $table) { $table->id(); $table->string('name'); ... });
// Schema::create('posts', function (Blueprint $table) { $table->id(); $table->foreignId('user_id'); ... });
// Schema::create('comments', function (Blueprint $table) { $table->id(); $table->morphs('commentable'); $table->integer('rating')->default(0); $table->boolean('approved')->default(false); ... });
// Schema::create('orders', function (Blueprint $table) { $table->id(); $table->foreignId('user_id'); $table->decimal('amount', 8, 2); $table->string('status'); ... });
How it works: Laravel Eloquent's `withCount()`, `withSum()`, `withAvg()`, etc., methods provide an efficient way to retrieve aggregate values for related models without actually loading the entire relationship. This significantly reduces the amount of data fetched from the database, preventing N+1 queries for aggregate values. The first example demonstrates `withCount('posts')` to get the total number of posts for each user. The second example uses `withSum()` with a conditional closure to calculate the sum of 'rating' for only 'approved' comments on each post. The aggregate results are accessible as attributes on the parent model (e.g., `$user->posts_count`, `$post->approved_comments_total_rating`). This technique is invaluable for performance optimization when displaying summary data.

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs