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

如何用Laravel Eloquent one()->ofMany()获取下一个或最后一个排期?

问题:如何用关联关系替代属性实现获取下一个或最后一个排期事件?

我有一个Schedule模型用于记录项目(如Movie电影)的发布日期,一部电影可被排期0次或多次。我需要获取:

  • 1)下一次排期时间(若无则获取上一次);
  • 2)上一次排期时间。

目前Movie模型中已定义如下关联关系:

public function lastScheduleEvent(): HasOne
{
    return $this->scheduleEvents()->one()->ofMany('starts_at','max');
}

public function nextScheduleEvent(): HasOne
{
    return $this->scheduleEvents()->one()->ofMany([
        'starts_at' => 'max'
    ], function (Builder $query) {
        $query->where('starts_at','>',now());
    }
    );
}

同时通过属性获取下一个或最后一个排期:

protected function nextOrLastScheduleEvent(): Attribute
{
    return Attribute::make(
        get: fn ($value) => $this->nextScheduleEvent ?? $this->lastScheduleEvent
    );
}

请问能否创建一个one()->ofMany(...)关联关系(替代属性)来获取nextOrLastScheduleEvent?即是否存在可结合min/max聚合函数的查询来实现获取下一个或最后一个排期事件?


回答

可以通过自定义排序逻辑结合ofMany闭包实现这个关联关系,核心是给排期事件设定优先级:优先选择未来的最近排期,无未来排期时选择过去的最近排期(最后一次)。

实现方案1:带优先级字段的排序

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\HasOne;

public function nextOrLastScheduleEvent(): HasOne
{
    return $this->scheduleEvents()->one()->ofMany(
        [
            'starts_at' => function ($query) {
                // 先按是否为未来排期设优先级,再按时间差绝对值排序
                $query->selectRaw('CASE WHEN starts_at > NOW() THEN 0 ELSE 1 END as priority, starts_at')
                      ->orderBy('priority')
                      ->orderByRaw('ABS(TIMESTAMPDIFF(SECOND, starts_at, NOW()))');
            }
        ]
    );
}

实现方案2:简化的直接排序表达式

如果不想额外选择字段,可直接在排序逻辑中用表达式:

public function nextOrLastScheduleEvent(): HasOne
{
    return $this->scheduleEvents()->one()->ofMany(
        'starts_at',
        function (Builder $query) {
            $query->orderByRaw('CASE WHEN starts_at > NOW() THEN 0 ELSE 1 END')
                  ->orderByRaw('ABS(TIMESTAMPDIFF(SECOND, starts_at, NOW()))');
        }
    );
}

逻辑说明

  • 优先级排序:未来排期(starts_at > NOW())优先级设为0,过去排期设为1,确保未来排期优先被选中。
  • 时间差排序:按当前时间与排期时间的秒数差绝对值排序,保证未来排期取最近的,过去排期取最后一次。
  • ofMany会自动获取排序后的第一条记录,完全符合「有下一次则取下一次,无则取上一次」的需求。

注意:如果使用非MySQL数据库(如PostgreSQL),需要替换TIMESTAMPDIFF为对应时间差函数,比如PostgreSQL可用EXTRACT(EPOCH FROM (starts_at - NOW()))计算秒数差。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 13:32:20