【问题标题】:Find Customers who have birthdays in next 7 days in laravel在 laravel 中查找未来 7 天内过生日的客户
【发布时间】:2019-06-20 04:42:10
【问题描述】:

我需要在 laravel 中获取未来 7 天内过生日的客户列表。我使用以下查询。该查询显示也过了生日。

 $customerslist=Customers::WhereRaw('DAYOFYEAR(curdate()) <= DAYOFYEAR(date_of_birth) AND DAYOFYEAR(curdate()) + 7 >=  dayofyear(date_of_birth)' )->OrderBy(DB::raw("DAYOFYEAR(date_of_birth)"),'ASC')->get();

我使用下面列出的查询但缺少一些客户

缺少客户

【问题讨论】:

  • 你没有尝试在 Laravel 中使用 whereBetween() 吗?
  • 不要像这样使用 DAYOFYEAR。 DAYOFYEAR("2018-12-31") 是 365,但 DAYOFYEAR("2019-01-01") 是 1。

标签: php laravel laravel-5.5 laravel-5.8


【解决方案1】:

我发现使用 DAYOFYEAR 方法的原始查询非常优雅,因为它消除了不同出生年份的所有问题。但是,它不能很好地处理闰年。

我发现 BETWEEN 和 DATE_FORMAT 的组合会产生正确的结果。 它确实需要一个检查来处理多年来的边缘。

以下是如何在模型范围内实现此功能的示例:

public function scopeBirthDayBetween ($query, Carbon $from, Carbon $till)
{
    $fromMonthDay = $from->format('m-d');
    $tillMonthDay = $till->format('m-d');
    if ($fromMonthDay <= $tillMonthDay) {
        //normal search within the one year
        $query->whereRaw("DATE_FORMAT(birthdate, '%m-%d') BETWEEN '{$fromMonthDay}' AND '{$tillMonthDay}'");
    } else {
        //we are overlapping a year, search at end and beginning of year
        $query->where(function ($query) use ($fromMonthDay, $tillMonthDay) {
            $query->whereRaw("DATE_FORMAT(birthdate, '%m-%d') BETWEEN '{$fromMonthDay}' AND '12-31'")
                ->orWhereRaw("DATE_FORMAT(birthdate, '%m-%d') BETWEEN '01-01' AND '{$tillMonthDay}'");
        });
    }
}

用法:

$users = User::birthDayBetween(Carbon::now(), Carbon::now()->addWeek())->get()

要在出生而不是出生日期添加排序,更多原始查询和 DATE_FORMAT 会有所帮助。

$users = User::query()
    ->birthDayBetween($from, $till)
    ->orderByRaw("DATE_FORMAT(birth_date,'%m%d')")
    ->orderByRaw("DATE_FORMAT(birth_date,'%y') desc")
    ->orderBy('first_name')
    ->get();

注意:在查看年份边缘时,订购将被关闭,因为在明年年初生日的用户将被优先订购。 我选择在前端解决这个问题。

【讨论】:

    【解决方案2】:

    我的解决方案:

    在你的用户模型上

        use Carbon\CarbonPeriod;
    
        public function scopeBirthdayBetween($query, $dateBegin, $dateEnd)
        {
            $monthBegin = explode('-', $dateBegin)[0];
            $dayBegin = explode('-', $dateBegin)[1];
            $monthEnd = explode('-', $dateEnd)[0];
            $dayEnd = explode('-', $dateEnd)[1];
            $currentYear = date('Y');
    
            $period = CarbonPeriod::create("$currentYear-$monthBegin-$dayBegin", "$currentYear-$monthEnd-$dayEnd");
    
            foreach ($period as $key => $date) {
                $queryFn = function($query) use ($date) {
                    $query->whereMonth("birthday", '=', $date->format('m'))->whereDay("birthday", '=', $date->format('d'));
                };
    
                if($key === 0) {
                    $queryFn($query);
                } else {
                    $query->orWhere(function($q) use ($queryFn) {
                        $queryFn($q);
                    });
                }
            }
    
            return $query;
        }
    

    用法:

       User::birthdayBetween('01-30', '02-15')->get()
    

    【讨论】:

      【解决方案3】:

      您可以尝试阅读laravel ORM 文章。

      使用 whereBetween 选择日期,使用 Carbon 日期更容易阅读。

      return $this->model
              ->whereBetween('dateOfBirth_Column', [Carbon::today()->toDate(), Carbon::today()->addDays(7)->toDate()])
              ->orderBy('dateOfBirth_Column')
              ->get();
      

      希望这会有所帮助。

      【讨论】:

      • 它不会工作,因为它只会搜索当前年份
      • @Manojkiran.A 不正确。 Carbon 可以准确地处理年份翻转。
      • @BenSholdice 如果出生日期列在数据库中,例如 1990-06-21,那么 Carbon::today()-&gt;addDays(7) 不会捕获到这个。尽管严格来说生日是明天,明天就是明天,但是 29 年前。看到问题了吗?每年都有生日,这就是为什么使用DAYOFYEAR()
      • @TimLewis 啊,是的,你是对的。即便如此,DAYOFYEAR() 使用的原始海报很糟糕,无法处理年份翻转。
      • @BenSholdice 这也是正确的,但只是一个极端情况。为此,您可能需要使用 IN(365, 1, 2, 3, 4, 5, 6) 或其他东西。
      【解决方案4】:

      正如@TimLewis 在 cmets 中指出的那样,您的模型有一个 date-of-birth 字段,并且您正在使用它与当前日期(可能是出生日期之后的许多年)进行比较。

      php解决方案:

      为了可读性和处理年份翻转问题,您应该:

      1. 使用 Carbon 进行日期和日期数学运算
      2. 查找用户的下一个/即将到来的生日
      3. 请记住,Y-m-d 格式通常意味着时间为 00:00:00。
      $now = Carbon::now();
      $customers = Customer::all();
      
      // find next birthday for each customer
      foreach ($customers as $customer) {
          $curyear_bd = Carbon::createFromFormat('Y-m-d', $customer->date_of_birth)->setYear($now->year);
          $now > $curyear_bd->endOfDay() ? $next_bd = $curyear_bd->addYear(1) : $next_bd = $curyear_bd;
      
          if ($now <= $next_bd->startOfDay() && $next_bd <= Carbon::now()->addDay(7)->endOfDay())
          {
              print "customer $customer->name has birthday coming up\n";
          }
      }
      

      MySQL 解决方案

      您可以像这样使用 SQL 查询:

      select * from customers where
      if (
      CURDATE() >= date_add(date_of_birth, interval  TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE())    year), 
                   date_add(date_of_birth, interval (TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE())+1) year), 
                   date_add(date_of_birth, interval  TIMESTAMPDIFF(YEAR, date_of_birth, CURDATE())    year)
      )
      between
      curdate()
      and
      date_add(curdate(), interval 7 day);
      

      但我会警惕时区(服务器、客户端等)。这可能需要它自己的 SO 问题...

      我从Get difference in years between two dates in MySQL as an integer找到了TIMESTAMPDIFF

      【讨论】:

        【解决方案5】:

        使用碳日期

        $customerslist= Customers::whereDate('date_of_birth', 'addDays(7))->get();

        【讨论】:

          【解决方案6】:

          我添加了原始查询,请检查它是否有效并告诉我

           $customerslist=Customers::WhereRaw(' date_of_birth >= DATEADD(day,-7, GETDATE())' )->get();
          

          希望对你有帮助

          【讨论】:

            【解决方案7】:

            对于即时解决方案,我创建了原始查询

            // start range 7 days ago
            $start = date('z') + 1 - 7;
            // end range 7 days from now
            $end = date('z') + 1 + 7;
            $customerslist = Customers::whereRaw("DAYOFYEAR(birthday) BETWEEN $start AND $end")->get();
            

            已编辑

            所以我用随机年份和日期创建了 5000 个记录

            MethodOne 使用 Eloquent filter

            $checkRange =   array_map(
                    function ($date) {
                        return $date->format('Y-m-d');
                    },
                    \Carbon\CarbonPeriod::create(now(), now()->addDays(7))->toArray()
                );
            
            
                $carbonWay = Customer::get()->filter(function($eachCus) use ( $checkRange){
                    return in_array( $eachCus->dob, $checkRange);
                });
            

            但如果你有许多客户,它会加热 sql server

            方法二QueryBuilder方式

             $eloquentway = Customer::whereDate('dob','>=', now())
                                ->whereDate( 'dob', '<=', now()->addDays(7))
                                ->get();
            

            所以这两个结果对我来说都是相同的结果

            最后我检查了 ids 是否有数学所以

            $idsofCarbon = $carbonWay->pluck('id')->toArray();
                $idsofFilter = $elequentway->pluck('id')->toArray();
            
                dump(array_diff( $idsofFilter, $idsofCarbon));
                dump(array_diff( $idsofCarbon, $idsofFilter));
            

            两者都给我[] 这意味着结果是准确的

            有关日期过滤的更多信息

            https://laraveldaily.com/eloquent-date-filtering-wheredate-and-other-methods/ https://laravel.com/docs/5.8/queries#where-clauses

            如有问题欢迎评论

            希望对你有帮助

            【讨论】:

            • 我使用了您的查询,但未列出所有客户
            • 好的,我将创建一些假数据并在 myside 进行测试并编辑答案
            • Date 格式应该是这样 format('m-d'),return 应该是这样 return in_array( date('m-d', strtotime($eachCus->dob)), $检查范围);因为由于出生年份的变化,dob 与即将到来的日子不匹配
            【解决方案8】:

            我遇到了这个挑战并以这种方式解决了它:

            $next_days = now()->addDays(7);
            $customers = Customers::query()->whereMonth('date_of_birth', $next_days->month)->whereDay('date_of_birth',$next_days->day)->get();
            

            【讨论】:

            • 这只会给出从现在起第 7 天生日的客户列表。该问题需要所有未来 7 天内过生日的客户,因此您必须进行 whereBetween 查询。
            【解决方案9】:

            一个可能的解决方案是这样的

            $customerslist = Customers::whereRaw("date_of_birth BETWEEN NOW() AND DATE_ADD(NOW(), INTERVAL 7 DAY")->get()

            【讨论】:

              猜你喜欢
              • 2014-03-29
              • 1970-01-01
              • 1970-01-01
              • 2018-11-30
              • 1970-01-01
              • 2010-11-01
              • 1970-01-01
              • 2015-05-21
              • 2011-09-02
              相关资源
              最近更新 更多