You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel Eloquent预加载时如何获取关联模型列的求和值?

如何在Laravel Eloquent预加载时获取关联模型的求和值

嘿,这个思路真的很赞——直接在预加载阶段就计算关联数据的聚合值,确实能大幅减少数据库返回的数据量,比加载所有关联记录要高效太多了!不过你给出的示例写法没法直接生效,因为with()方法里的字段选择语法不支持直接写聚合函数。别担心,我们有两种靠谱的解决方案来实现这个需求:

方法一:通过闭包定制预加载查询

你可以在with()的关联闭包里,用DB::raw()来编写聚合逻辑,同时要记得包含关联外键并分组,这样Eloquent才能正确把聚合结果和对应的Website模型关联起来:

use Illuminate\Support\Facades\DB;

$websites = Website::with([
    // 处理valid_click_ads的求和
    'valid_click_ads' => function ($query) {
        $query->select(
            'website_id', // 必须包含外键,用于关联匹配
            DB::raw('COALESCE(SUM(clicks), 0) as total_clicks'), // COALESCE确保无数据时返回0而非null
            DB::raw('COALESCE(SUM(revenue), 0) as total_revenue')
        )->groupBy('website_id'); // 按网站分组计算总和
    },
    // 处理facebook_ads的求和
    'facebook_ads' => function ($query) {
        $query->select(
            'website_id',
            DB::raw('COALESCE(SUM(clicks), 0) as total_clicks'),
            DB::raw('COALESCE(SUM(impressions), 0) as total_impressions')
        )->groupBy('website_id');
    },
    // 处理google_ads的求和
    'google_ads' => function ($query) {
        $query->select(
            'website_id',
            DB::raw('COALESCE(SUM(clicks), 0) as total_clicks'),
            DB::raw('COALESCE(SUM(impressions), 0) as total_impressions')
        )->groupBy('website_id');
    }
])->get();

使用时,你可以通过关联属性访问聚合值:

foreach ($websites as $website) {
    echo $website->valid_click_ads->first()->total_clicks;
    echo $website->facebook_ads->first()->total_impressions;
}

方法二:使用withCount()扩展实现聚合

如果你不需要保留关联模型的结构,只是想把聚合值直接作为Website模型的属性,withCount()是更简洁的选择——它不仅能计数,还支持自定义聚合函数:

use Illuminate\Support\Facades\DB;

$websites = Website::withCount([
    // 给valid_click_ads的clicks求和,命名为valid_clicks_total
    'valid_click_ads as valid_clicks_total' => function ($query) {
        $query->select(DB::raw('COALESCE(SUM(clicks), 0)'));
    },
    // 给valid_click_ads的revenue求和,命名为valid_revenue_total
    'valid_click_ads as valid_revenue_total' => function ($query) {
        $query->select(DB::raw('COALESCE(SUM(revenue), 0)'));
    },
    // facebook_ads的clicks求和
    'facebook_ads as fb_clicks_total' => function ($query) {
        $query->select(DB::raw('COALESCE(SUM(clicks), 0)'));
    },
    // facebook_ads的impressions求和
    'facebook_ads as fb_impressions_total' => function ($query) {
        $query->select(DB::raw('COALESCE(SUM(impressions), 0)'));
    },
    // google_ads的clicks求和
    'google_ads as g_clicks_total' => function ($query) {
        $query->select(DB::raw('COALESCE(SUM(clicks), 0)'));
    },
    // google_ads的impressions求和
    'google_ads as g_impressions_total' => function ($query) {
        $query->select(DB::raw('COALESCE(SUM(impressions), 0)'));
    }
])->get();

这种方式下,聚合值会直接成为Website实例的属性,使用起来更直观:

foreach ($websites as $website) {
    echo $website->valid_clicks_total;
    echo $website->fb_impressions_total;
}

注意事项

  • 用COALESCE包裹SUM是为了避免没有关联记录时返回null,确保值始终是数字类型,方便后续页面渲染。
  • 如果关联表使用了软删除,记得在闭包里加上->withTrashed()(如果需要包含已删除记录),否则会自动排除软删除的数据。
  • 对于数据量较大的关联表,建议给website_id和聚合字段(比如clicks、impressions)创建复合索引,提升聚合查询的性能。

内容的提问来源于stack exchange,提问作者LucyTurtle

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:38:33