You need to enable JavaScript to run this app. Laravel 多对多关联透视表如何筛选最大日期关联记录
优惠活动
大模型
产品
解决方案
定价
更多

Laravel 多对多关联透视表如何筛选最大日期关联记录

原有代码失效原因

你之前的写法无法生效核心有两个逻辑错位:

  • $invoices是Invoice模型的顶层集合,而pivot.shift_swapped_date是挂在每个被关联出来的Shift模型实例上的属性,不是Invoice本身的字段。Laravel集合的max()方法只会遍历集合顶层元素的属性取值,根本不会深入嵌套的关联集合内部取数,自然无法拿到预期结果。
  • 就算遍历能触达内层关联,直接全量加载所有关联记录的方式,会把每个Invoice对应的全部shift关联都读进内存,再手动找最大值,大数据量下会产生不必要的性能损耗。
正确实现方案

方案1:关联层直接约束取最大日期记录(推荐,性能最优)

不需要全量加载所有关联,直接在关联定义里加数据库层的过滤条件,只取对应Invoice下透视表shift_swapped_date最大的那条关联记录。
先在Invoice模型里新增专用关联方法:

// 新增在Invoice模型内部
public function maxSwapShift()
{
    return $this->belongsToMany(Shift::class, 'invoice_shift')
        ->withPivot([
            'invoice_id',
            'shift_id',
            'shift_taken_id',
            'shift_swapped_date',
            'shift_taken_date',
            'tas',
            'msg',
            'status_tas'
        ])
        // 数据库层直接匹配透视表最大日期,避免全量加载关联
        ->whereRaw('invoice_shift.shift_swapped_date = (
            SELECT MAX(shift_swapped_date)
            FROM invoice_shift
            WHERE invoice_shift.invoice_id = invoice.id
        )');
}

控制器中预加载这个关联即可直接拿到每个Invoice对应的目标记录:

$invoices = Invoice::with('maxSwapShift')->get();

// 遍历取值示例
foreach ($invoices as $invoice) {
    $maxShiftDate = $invoice->maxSwapShift->first()->pivot->shift_swapped_date ?? null;
}

注意:

  • 如果存在多条透视记录的shift_swapped_date和最大值持平,这个查询会返回所有同值记录。如果只需要固定取一条,可额外加排序限制:
    ->orderByDesc('invoice_shift.id')
    ->limit(1)
    
  • 你当前在Shift模型的invoices关联中没有配置withPivot,如果需要从Shift侧反向查询关联Invoice并读取透视表自定义字段,需要在对应belongsToMany方法中补充和Invoice侧一致的withPivot参数,否则无法读取到透视表值。

方案2:全量加载关联后手动遍历取值(适合小数据量场景)

如果不想改动模型关联定义,在已全量加载shifts关联的前提下,需要做两层遍历才能取到值,不能直接对顶层Invoice集合取max:

$invoices = Invoice::with('shifts')->get();

foreach ($invoices as $invoice) {
    // 从当前Invoice自己的shifts关联集合中,提取透视字段的最大值
    $maxSwapDate = $invoice->shifts->max('pivot.shift_swapped_date');
    // 匹配对应最大值的关联记录
    $maxSwapRecord = $invoice->shifts
        ->where('pivot.shift_swapped_date', $maxSwapDate)
        ->first();
}

这种写法的缺点是会加载所有关联记录到内存,数据量大时性能较差。

额外补充:如果你的需求不是为每个Invoice单独取自己的最大日期,而是要从所有Invoice关联的全部透视记录中,找全局的最大shift_swapped_date,不需要遍历关联,直接查透视表即可:

use Illuminate\Support\Facades\DB;
$globalMaxDate = DB::table('invoice_shift')->max('shift_swapped_date');

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.02 22:45:04