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

Laravel中如何检索含重复规则的日历日期记录

解决方案

核心思路

  1. 调整查询逻辑,覆盖两种场景:selected_date本身在起止日期范围内,或者selected_date在结束日期之前且存在重复规则(可能生成落在范围内的后续日期)。
  2. 基于Carbon日期工具,根据重复规则(日/周/月)生成所有符合条件的日期,同时限制两个边界:不超过重复次数上限(after_occurence_value)、不超过指定的结束日期。
  3. 筛选出落在起止日期范围内的日期,整理成目标格式后排序输出。

修改后的服务层代码

use Carbon\Carbon;

class CalendarTimeService 
{
    public function myquery()
    {
        $startDate = Carbon::parse('2025-02-01');
        $endDate = Carbon::parse('2025-02-28');

        // 获取所有可能生成有效日期的CalendarTime记录
        $calendarTimes = CalendarTime::where(function($query) use ($startDate, $endDate) {
            // 场景1:selected_date本身在起止范围内
            $query->whereBetween('selected_date', [$startDate, $endDate])
            // 场景2:selected_date在结束日期前,且存在重复规则(可能生成后续有效日期)
            ->orWhere(function($q) use ($endDate) {
                $q->where('selected_date', '<=', $endDate)
                  ->whereHas('recurrence');
            });
        })
        ->with('recurrence')
        ->get();

        $finalResult = [];

        foreach ($calendarTimes as $calendarTime) {
            $baseDate = Carbon::parse($calendarTime->selected_date);
            $recurrence = $calendarTime->recurrence;

            // 生成符合规则的所有日期
            $dates = $this->generateRecurringDates(
                $baseDate,
                $recurrence?->repeat_occurence,
                $recurrence?->repeat_every ?? 1,
                $recurrence?->after_occurence_value ?? 1,
                $startDate,
                $endDate
            );

            // 转换为目标格式
            foreach ($dates as $date) {
                $finalResult[] = [
                    'date' => $date->format('Y-m-d'),
                    'time_from' => $calendarTime->time_from,
                    'time_to' => $calendarTime->time_to,
                ];
            }
        }

        // 按日期排序
        usort($finalResult, function($a, $b) {
            return $a['date'] <=> $b['date'];
        });

        return $finalResult;
    }

    /**
     * 根据重复规则生成符合条件的日期
     * @param Carbon $baseDate 起始日期
     * @param string|null $repeatOccurence 重复单位(day/week/month)
     * @param int $repeatEvery 重复间隔数
     * @param int $maxOccurrences 最大重复次数
     * @param Carbon $startDate 查询起始日期
     * @param Carbon $endDate 查询结束日期
     * @return Carbon[]
     */
    private function generateRecurringDates(Carbon $baseDate, ?string $repeatOccurence, int $repeatEvery, int $maxOccurrences, Carbon $startDate, Carbon $endDate)
    {
        $dates = [];

        // 无重复规则时,仅检查起始日期是否在范围内
        if (!$repeatOccurence) {
            if ($baseDate->between($startDate, $endDate)) {
                $dates[] = $baseDate->copy();
            }
            return $dates;
        }

        $currentDate = $baseDate->copy();
        $occurrenceCount = 0;

        // 循环生成日期,直到达到最大次数或超过结束日期
        while ($occurrenceCount < $maxOccurrences && $currentDate <= $endDate) {
            // 仅保留在查询范围内的日期
            if ($currentDate >= $startDate) {
                $dates[] = $currentDate->copy();
            }

            // 根据重复单位计算下一个日期
            switch ($repeatOccurence) {
                case 'day':
                    $currentDate->addDays($repeatEvery);
                    break;
                case 'week':
                    $currentDate->addWeeks($repeatEvery);
                    break;
                case 'month':
                    $currentDate->addMonths($repeatEvery);
                    break;
                default:
                    // 未知重复规则,终止循环
                    break 2;
            }

            $occurrenceCount++;
        }

        return $dates;
    }
}

关键细节说明

  • 查询逻辑优化:避免漏掉selected_date在查询起始日之前,但重复后生成的日期落在范围内的记录。
  • 日期边界控制:生成日期时同时满足两个限制:不超过after_occurence_value指定的最大次数,不超过查询的结束日期。
  • Carbon工具使用:利用Carbon的日期加减、比较方法,简化日期操作的复杂度。
  • 结果排序:最终结果按日期升序排列,保证输出顺序符合预期。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 12:31:04