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

Laravel Eloquent 实现首次客户的 upcoming 预约查询与排序

Eloquent实现携带即将到来预约的首次消费客户查询

你完全可以在现有Customer查询链上叠加条件实现需求,不需要反向从Reservation发起查询,最终直接返回携带关联预约的Customer集合,两个待解决问题都可以通过Laravel内置的关联查询能力搞定:

  • 无历史预约的首次客户筛选:用whereDoesntHave排除存在过去预约记录的客户
  • 按预约时间排序:用关联子查询取出每个客户对应的最早未来预约时间,作为主查询的排序依据,同时避免一对多关联带来的记录重复问题

可直接运行的完整查询代码如下:

use Carbon\Carbon;
use App\Models\Customer;
use App\Models\Reservation;

$now = Carbon::now();

$customerList = Customer::query()
    // 筛选存在即将到来预约的客户
    ->whereHas('reservation', function ($query) use ($now) {
        $query->where('date', '>', $now);
    })
    // 排除存在任意历史预约的客户,命中首次消费客户的筛选规则
    ->whereDoesntHave('reservation', function ($query) use ($now) {
        $query->where('date', '<=', $now);
    })
    // 按客户最早的即将到来预约时间排序
    ->orderBy(
        Reservation::select('date')
            ->whereColumn('reservation.customer_id', 'customer.id')
            ->where('date', '>', $now)
            ->orderBy('date')
            ->limit(1),
        'asc'
    )
    // 加载客户关联的所有即将到来的预约,关联数据本身按预约时间排序
    ->with(['reservation' => function ($query) use ($now) {
        $query->where('date', '>', $now)->orderBy('date', 'asc');
    }])
    ->get();

关键实现说明

  • 首次客户筛选逻辑:whereDoesntHave会自动生成NOT EXISTS类型的SQL子查询,只要当前客户存在任意一条日期早于当前时间的预约记录,就会被排除,完全匹配“无任何历史预约的首次消费客户”的定义
  • 排序逻辑避坑:如果直接通过JOIN关联Reservation表做排序,因为是一对多关系,同一个客户存在多条未来预约时会被查出多次,导致Customer集合出现重复记录;用标量子查询取每个客户最早的未来预约时间排序,既能保证Customer记录的唯一性,也能达到按预约时间排序的效果
  • 关联加载逻辑:在with加载关联数据时同样叠加了时间过滤条件,不会把已经被筛选排除的历史预约记录加载到结果集中,关联预约集合本身也按时间升序排列,和主查询的排序规则保持一致

你之前考虑的从Reservation模型反向发起查询的方案,即使加上“客户无历史预约”的筛选条件,最终返回的根模型是Reservation,后续还需要手动做客户去重、集合结构重组才能拿到你想要的Customer根集合,相比直接在Customer层做查询的方案,额外代码量大、维护成本高,整体性价比很低。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.28 03:45:04