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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 20:10:19