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

如何筛选出最新关联消息未读的Offer记录?

解决Laravel中筛选「最新消息未读」的Offer记录问题

你的问题核心在于:whereHas('latestMessage')只会检查关联的messages中是否存在未读消息,而非验证最新的那条消息是否未读。以下是几种可行的解决方案:


方法1:使用whereExists子查询精准匹配

直接通过子查询校验每个Offer对应的最新消息是否未读,逻辑清晰且性能较好:

Offer::whereExists(function ($query) {
    $query->select(DB::raw(1))
          ->from('messages')
          ->whereColumn('messages.seller_id', 'offers.seller_id')
          ->whereNull('messages.read')
          ->whereRaw('messages.created_at = (SELECT MAX(created_at) FROM messages WHERE seller_id = offers.seller_id)');
})->paginate(100);

逻辑说明:对每个Offer,检查是否存在一条属于它的消息——这条消息既未读,同时又是该Offer下创建时间最晚的记录。


方法2:基于现有关联调整whereHas查询

如果你想保留whereHas的写法,可以在闭包内添加子查询,强制限定仅校验最新的那条消息:

Offer::whereHas('latestMessage', function ($query) {
    $query->whereNull('read')
          ->whereRaw('created_at = (SELECT MAX(created_at) FROM messages WHERE seller_id = offers.seller_id)');
})->paginate(100);

注:原latestMessage关联的latest()仅在获取单条关联数据时生效,whereHas默认会遍历所有关联记录,因此必须通过子查询锁定最新的那条。


方法3:用JOIN关联最新消息记录

通过两次JOIN先定位每个Offer的最新消息,再筛选未读状态:

Offer::join(DB::raw('(SELECT seller_id, MAX(created_at) as max_created_at FROM messages GROUP BY seller_id) as latest_messages'), function ($join) {
    $join->on('offers.seller_id', '=', 'latest_messages.seller_id');
})->join('messages', function ($join) {
    $join->on('messages.seller_id', '=', 'offers.seller_id')
         ->on('messages.created_at', '=', 'latest_messages.max_created_at');
})->whereNull('messages.read')
  ->select('offers.*')
  ->distinct()
  ->paginate(100);

逻辑说明:先通过子查询获取每个seller_id对应的最新消息创建时间,再关联到messages表找到具体的最新消息,最后筛选出未读的Offer(用distinct避免重复)。


原写法失效原因

你之前的latestMessage关联用hasOne(...) ->latest(),仅在获取单个Offer的关联数据时会返回最新消息;但whereHas的逻辑是检查「该Offer是否存在任意符合条件的关联消息」,不会自动沿用关联定义中的latest()限制,因此会误判为只要有未读消息就返回。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 01:50:24