Laravel模型本地作用域:基于最新关联Reply筛选Ticket问题
解决方案
问题根源
你用hasMany定义lastVisibleReply并加limit(1)的方式,在whereHas中不会生效——因为whereHas的核心逻辑是检查关联表中是否存在符合条件的记录,而非针对单条关联记录做筛选,Laravel会自动忽略关联定义中的limit、orderBy这类限制单条结果的条件,转而遍历所有关联的Reply。
方案一:改用hasOne定义最新回复关联(推荐)
将获取“最新可见回复”的关联从hasMany改为hasOne,因为我们只需要单条最新记录,hasOne会自动保留排序后的第一条结果,且在whereHas中能正确应用关联条件:
// Ticket模型中定义关联 public function lastVisibleReply() { // hasOne + where筛选可见回复 + latest()按创建时间取最新 return $this->hasOne(Reply::class) ->where('is_hidden', 0) ->latest('created_at'); } // 定义本地作用域:排除最新回复由当前用户发送的工单 public function scopeExcludeMyLatestReply($query, $currentUserId) { return $query->whereHas('lastVisibleReply', function ($replyQuery) use ($currentUserId) { $replyQuery->where('user_id', '!=', $currentUserId); }); }
使用方式:
// 获取最新回复不是当前用户的工单 $tickets = Ticket::excludeMyLatestReply(auth()->id())->get();
方案二:用子查询直接筛选(性能更优)
如果需要更高效的查询,可直接用whereNotExists子查询,避免关联查询的额外开销:
use Illuminate\Support\Facades\DB; public function scopeExcludeMyLatestReply($query, $currentUserId) { return $query->whereNotExists(function ($subQuery) use ($currentUserId) { $subQuery->select(DB::raw(1)) ->from('replies') // 关联工单ID ->whereColumn('replies.ticket_id', 'tickets.id') // 筛选可见回复 ->where('replies.is_hidden', 0) // 当前用户发送的回复 ->where('replies.user_id', $currentUserId) // 确保是该工单的最新可见回复 ->whereRaw('replies.created_at = (SELECT MAX(created_at) FROM replies WHERE ticket_id = tickets.id AND is_hidden = 0)'); }); }
验证方法
可以用toSql()查看生成的SQL语句,确认条件是否正确:
dd(Ticket::excludeMyLatestReply(auth()->id())->toSql());
内容的提问来源于stack exchange,提问作者Mostafa Lotfi
相关产品推荐
相关产品推荐

