本文目录导读:

在Laravel中,withCount()是一个非常实用的聚合方法,用于统计关联模型的数量,让我详细介绍一下这个方法和相关的聚合功能。
withCount() 基本用法
基础场景
// 假设有 User 和 Post 模型,用户有多个帖子
$users = User::withCount('posts')->get();
// 结果中会自动添加 posts_count 字段
foreach ($users as $user) {
echo $user->posts_count;
}
多条件统计
// 统计已发布的帖子数量
$users = User::withCount(['posts' => function ($query) {
$query->where('status', 'published');
}])->get();
// 多关联统计
$users = User::withCount(['posts', 'comments', 'likes'])->get();
其他聚合方法
withSum() - 求和
// 计算用户的订单总金额
$users = User::withSum('orders', 'total_amount')->get();
// 可以用 $user->orders_sum_total_amount 访问
// 带条件
$users = User::withSum(['orders' => function ($query) {
$query->where('status', 'completed');
}], 'total_amount')->get();
withMax() / withMin() - 最大值/最小值
// 获取用户最高订单金额
$users = User::withMax('orders', 'total_amount')->get();
// 获取用户最低订单金额
$users = User::withMin('orders', 'total_amount')->get();
withAvg() - 平均值
// 获取用户订单的平均金额
$users = User::withAvg('orders', 'total_amount')->get();
完整示例
// 电商系统中的综合统计
class User extends Model
{
public function orders()
{
return $this->hasMany(Order::class);
}
public function products()
{
return $this->hasMany(Product::class);
}
}
// 复杂统计查询
$users = User::withCount(['orders' => function ($query) {
$query->where('status', 'completed');
}])
->withSum(['orders' => function ($query) {
$query->where('status', 'completed');
}], 'total_amount')
->withAvg('orders', 'total_amount')
->withMax('orders', 'created_at')
->get();
foreach ($users as $user) {
echo "订单数: " . $user->orders_count;
echo "总金额: " . $user->orders_sum_total_amount;
echo "平均金额: " . $user->orders_avg_total_amount;
echo "最近订单: " . $user->orders_max_created_at;
}
关联别名和分组
自定义别名
$posts = Post::withCount([
'comments as approved_comments_count' => function ($query) {
$query->where('approved', true);
},
'comments as pending_comments_count' => function ($query) {
$query->where('approved', false);
}
])->get();
复合条件统计
$users = User::withCount([
'posts' => function ($query) {
$query->where('status', 'published');
},
'posts as draft_posts_count' => function ($query) {
$query->where('status', 'draft');
},
'posts as total_posts_count'
])->get();
高级用法
与作用域结合
class Post extends Model
{
public function scopePublished($query)
{
return $query->where('status', 'published');
}
}
// 控制器中
$users = User::withCount(['posts' => function ($query) {
$query->published(); // 使用作用域
}])->get();
多层级关联
// 统计用户文章被评论的数量
$users = User::withCount(['posts.comments as posts_comments_count'])->get();
// 或更复杂
$users = User::withCount([
'posts' => function ($query) {
$query->whereHas('comments', function ($q) {
$q->where('approved', true);
});
}
])->get();
子查询聚合
// 使用子查询计算自定义统计
$users = User::select('*')
->selectSub(function ($query) {
$query->from('orders')
->whereColumn('orders.user_id', 'users.id')
->where('status', 'completed')
->selectRaw('SUM(total_amount)');
}, 'completed_orders_total')
->get();
性能优化建议
索引优化
// 确保外键字段有索引
Schema::table('posts', function ($table) {
$table->index('user_id');
});
// 对统计的字段添加索引
Schema::table('orders', function ($table) {
$table->index('status');
$table->index('total_amount');
});
限制查询数量
// 只统计必要的记录
$users = User::withCount(['posts'])
->where('active', true)
->limit(100)
->get();
使用缓存
// 对频繁统计的数据进行缓存
$users = Cache::remember('users_with_post_count', 3600, function () {
return User::withCount('posts')->get();
});
实际应用案例
博客系统
// 文章列表页展示统计
$posts = Post::withCount(['comments', 'likes', 'views'])
->withSum('likes', 'count')
->with(['author:id,name'])
->latest()
->paginate(10);
foreach ($posts as $post) {
echo "标题: " . $post->title;
echo "评论数: " . $post->comments_count;
echo "点赞数: " . $post->likes_count;
echo "浏览数: " . $post->views_count;
}
数据分析
// 用户行为分析
$users = User::withCount([
'login_logs as total_logins',
'orders as total_orders',
'orders as completed_orders' => function ($query) {
$query->where('status', 'completed');
}
])
->withSum('orders', 'grand_total')
->withAvg('orders', 'grand_total')
->get();
// 筛选高价值用户
$vipUsers = User::withSum('orders', 'grand_total')
->having('orders_sum_grand_total', '>', 10000)
->get();
注意事项
-
性能考虑:大量聚合统计可能影响性能,建议注意索引
-
字段命名:生成的字段名为
{关联名}_{聚合方法}_{字段名}格式 -
查询限制:
having只能用于聚合字段 -
关联约束:
withCount中的闭包只能约束关联查询,不能影响主查询
这些聚合方法非常实用,能大大简化统计代码,提高开发效率,在实际项目中,合理使用这些方法可以避免很多复杂的原生SQL查询。