PHP
Efficiently Aggregate Related Data with `withCount`, `withSum`, and `withMax`
Optimize database queries by aggregating related model data like counts, sums, and averages directly within the parent query using Eloquent's `withCount`, `withSum`, and `withMax`.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
protected $fillable = ['title'];
public function comments()
{
return $this->hasMany(Comment::class);
}
}
class Comment extends Model
{
protected $fillable = ['post_id', 'content', 'likes'];
public function post()
{
return $this->belongsTo(Post::class);
}
}
// Setup some dummy data
$post1 = Post::create(['title' => 'My First Post']);
$post1->comments()->create(['content' => 'Great post!', 'likes' => 10]);
$post1->comments()->create(['content' => 'Loved it.', 'likes' => 5]);
$post2 = Post::create(['title' => 'Another Post']);
$post2->comments()->create(['content' => 'Interesting read.', 'likes' => 20]);
// Get posts with their comments count
$postsWithCounts = Post::withCount('comments')->get();
foreach ($postsWithCounts as $post) {
echo "Post: {$post->title}, Comments Count: {$post->comments_count}
";
}
echo "
";
// Get posts with total likes on comments, aliasing the result column
$postsWithLikesSum = Post::withSum('comments as total_comment_likes', 'likes')->get();
foreach ($postsWithLikesSum as $post) {
echo "Post: {$post->title}, Total Comment Likes: {$post->total_comment_likes}
";
}
echo "
";
// Get posts with the latest comment creation date
$postsWithLatestComment = Post::withMax('comments as latest_comment_at', 'created_at')->get();
foreach ($postsWithLatestComment as $post) {
echo "Post: {$post->title}, Latest Comment At: {$post->latest_comment_at}
";
}
echo "
";
// Multiple aggregations in a single query
$postsWithAggregates = Post::withCount('comments')
->withSum('comments as total_likes', 'likes')
->withAvg('comments as average_likes', 'likes')
->get();
foreach ($postsWithAggregates as $post) {
echo "Post: {$post->title}, Comments: {$post->comments_count}, Total Likes: {$post->total_likes}, Avg Likes: {$post->average_likes}
";
}
How it works: This snippet demonstrates how to use Eloquent's aggregation methods like `withCount`, `withSum`, `withMax`, and `withAvg` to efficiently retrieve aggregated data from related models. Instead of loading all related models and then performing calculations in PHP (which can lead to N+1 query issues), these methods execute a subquery to calculate the aggregate value directly within the main query. The result is a new attribute (e.g., `comments_count`, `total_comment_likes`) appended to the parent model instance, providing an optimized way to display summary data.