【问题标题】:Laravel - fill up missing dates and counts from databaseLaravel - 从数据库中填写缺失的日期和计数
【发布时间】:2019-11-25 02:46:16
【问题描述】:

我需要根据从 $request 获得的日期发送每个日期的观看次数。从我在控制器中的查询中,我得到如下图表的数据:

[{"time":"2016-05-01","count":2},{"time":"2016-05-02","count":3},{"time":"2016-05-03","count":7},{"time":"2016-05-07","count":3}]

对于该数据,我需要添加0 的缺失日期和查看次数。我正在尝试关注this example,但我似乎无法为我的示例实施此解决方案。这是我的方法:

public function timelines(Request $request){
    $from = $request['from'];
    $to = $request['to'];


    $data = DB::table($request['option'])
                ->select(DB::raw('DATE(created_at) as time'), DB::raw('count(*) as count'))
                ->whereDate('created_at', '>=', date($from).' 00:00:00')
                ->whereDate('created_at', '<=', date($to).' 00:00:00')
                ->groupBy('time')
                ->get();


    return json_encode($data);
  }

更新代码:

public function timelines(Request $request){
    $from = $request['from'];
    $to = $request['to'];
    $date = $from;
    $data = [];

    if ($request['option'] == 'time'){
      $data = View::groupBy('time')
               ->orderBy('time', 'ASC')
               ->whereDate('created_at', '>=', date($from).' 00:00:00')
               ->whereDate('created_at', '<=', date($to).' 00:00:00')
               ->get([
                 DB::raw('Hour(created_at) as time'),
                 DB::raw('COUNT(*) as "count"')
                 ]);
    }
    else {
      while($date <= $to) {
        $formattedDate = date('Y-m-d', strtotime($date));

        $results = DB::table($request['option'])
                   ->select(DB::raw('DATE(created_at) as time'), DB::raw('count(*) as count'))
                   ->whereDate('created_at', '=', $formattedDate .' 00:00:00')
                   ->groupBy('time')
                   ->get();

        if(count($results)) {
            $data[] = [
                'time' => $formattedDate,
                'count' => $results[0]->count
            ];
        } else {
            $data[] = [
                'time' => $formattedDate,
                'count' => 0
            ];
        }

        $date = strtotime('+1 day', strtotime($formattedDate));
      }
    }

    return json_encode($data);
  }

这样我只计算我发送的第一个日期,例如:

[{"time":"2016-03-16","count":0}]

【问题讨论】:

  • [{"time":"2016-03-16","count":0}] -> 这是您拥有$request['option'] == 'time'$request['option'] != 'time' 时得到的结果吗?
  • 当我有 $request['option'] != 'time' 时会发生这种情况,但我已经想出了一个解决方案,并且一切正常,就像我设置的那样。

标签: php mysql eloquent laravel-5.2


【解决方案1】:

这个解决方案对我有用,我首先创建了一个包含时间范围内所有日期的数组,日期设置为键,计数值设置为 0,然后在 DB 中替换查询后的值,相同具有来自查询的计数值的数组:

$period = new DatePeriod( new DateTime($from), new DateInterval('P1D'), new DateTime($to));
      $dbData = [];

      foreach($period as $date){
          $range[$date->format("Y-m-d")] = 0;
      }

      $data = DB::table($request['option'])
                ->select(DB::raw('DATE(created_at) as time'), DB::raw('count(*) as count'))
                ->whereDate('created_at', '>=', date($from).' 00:00:00')
                ->whereDate('created_at', '<=', date($to).' 00:00:00')
                ->groupBy('time')
                ->get();

      foreach($data as $val){
        $dbData[$val->time] = $val->count;
      }

      $data = array_replace($range, $dbData);
    }

    return json_encode($data);

【讨论】:

  • 嗨,我想使用相同的查询,但有更多的值,然后只计算,我需要做什么更新?
【解决方案2】:

@Marco 完美地回答了这个问题,但我想做一些改进。无需调用array_replace,而是可以在循环中执行此操作。像这样。

$period = new DatePeriod( new DateTime($from), new DateInterval('P1D'), new DateTime($to));
$dbData = [];

foreach($period as $date){
    $range[$date->format("Y-m-d")] = 0;
}

$data = DB::table($request['option'])
                ->select(DB::raw('DATE(created_at) as time'), DB::raw('count(*) as count'))
                ->whereDate('created_at', '>=', date($from).' 00:00:00')
                ->whereDate('created_at', '<=', date($to).' 00:00:00')
                ->groupBy('time')
                ->get();

foreach($data as $val){
    $range[$val->time] = $val->count; //Filling value in the array
}

//$data = array_replace($range, $dbData); <-- No need to do this.

return json_encode($range);

【讨论】:

  • 这是一种更有效、更清洁的解决方案。
【解决方案3】:

Maro 和 Rutvij Kothari 提供的答案是正确的,这是相同逻辑的更优雅/简化的版本。

我没有使用 2 个单独的 for 循环,而是使用 array_map 函数来做同样的事情。

$data = DB::table($request['option'])
    ->select(DB::raw('DATE(created_at) as time'), DB::raw('count(*) as count'))
    ->whereDate('created_at', '>=', date($from).' 00:00:00')
    ->whereDate('created_at', '<=', date($to).' 00:00:00')
    ->groupBy('time')
    ->get()
    ->keyBy('time');

$range = array_map(function ($e) use ($data) {
    $date = Carbon::parse($e)->format('Y-m-d');
    return empty($data[$date]) ? 0 : $data[$date]->count;
}, CarbonPeriod::create(Carbon::parse($from), Carbon::parse($to))->toArray());


return json_encode($range);

此外,如果您想生成过去 7 天等范围,您可以使用 Carbon 中的内置函数

CarbonPeriod::create(Carbon::now()->subDays(7), Carbon::now());

【讨论】:

    【解决方案4】:

    你可以遍历所有你想显示的日期并在一个while循环中计算它们:

    public function timelines(Request $request){
        $from = $request['from'];
        $to = $request['to'];
        $date = $from;
        $data = array();
    
        while($date <= $to) {
    
            $formattedDate = date('Y-m-d', $date);
    
            $results = DB::table($request['option'])
                       ->select(DB::raw('count(*) as count'))
                       ->whereDate('created_at', '=', $formattedDate .' 00:00:00')
                       ->groupBy('time')
                       ->get();
    
            if(count($results)) {
                $data[] = [
                    'time' => $formattedDate, 
                    'count' => $results[0]->count
                ];
            } else {
                $data[] = [
                    'time' => $formattedDate, 
                    'count' => 0
                ];
            }
    
            $date = strtotime('+1 day', $date);
        }
    
        return json_encode($data);
    }
    

    【讨论】:

    • 当你可以对集合进行循环时为什么要循环查询,这是一个非常糟糕的方法
    • @JimmyObonyoAbor 是的,你是对的。它根本没有优化。我试着给他一个简单的例子,让他继续前进而不是被困住。
    猜你喜欢
    • 1970-01-01
    • 2021-05-31
    • 1970-01-01
    • 1970-01-01
    • 2018-06-24
    • 1970-01-01
    • 2018-05-02
    • 2018-10-05
    相关资源
    最近更新 更多