Результат запроса группы Laravel по дням месяца-года (показать всю дату, если дата не существует)

#mysql #laravel #laravel-query-builder

Вопрос:

У меня есть столик exampleTable :

  ------------ -------- 
| created_at | result |
 ------------ -------- 
| 2021-05-21 |      3 |
| 2021-05-21 |      4 |
| 2021-05-22 |      5 |
| 2021-05-23 |      6 |
| 2021-05-23 |      7 |
 

Я хочу результата:

  ------------ -------- 
| day        | sumAll |
 ------------ -------- 
| 2021-05-01 |      0 |
| 2021-05-02 |      0 |
| 2021-05-03 |      0 |
....
| 2021-05-21 |      7 |
| 2021-05-22 |      5 |
| 2021-05-23 |     13 |
....
| 2021-05-29 |      0 |
| 2021-05-30 |      0 |
| 2021-05-21 |      0 |
 

И это мой вопрос:

 $results = ExampleTable::select(
    DB::raw("DATE_FORMAT(created_at,'%Y-%M-%d') as day"),
    DB::raw('sum(result) as sumAll')
)
->whereMonth("created_at", '05')
->whereYear("created_at", '2021')
->groupBy('day')
->get();
 

Но это не показывает, если дата не существует.
Я хочу, чтобы значение дня с датой и значением результата было равно 0, если данные не существуют

Комментарии:

1. Вы должны сгенерировать список дат (календарь), затем добавить в него свои данные и, наконец, подвести итоги. Метод генерации зависит от точной версии MySQL (или вы можете использовать статическую служебную таблицу с номерами от 1 до 31).

2. Является created_at ли текстовый столбец?

3. как использовать эквалайзер?. нет, это отметка времени, но я удаляю время в этом вопросе.

Ответ №1:

  1. Создайте новую коллекцию с помощью php artisan make:collection \App\Collections\ExampleCollection
  2. Сделайте следующее:
 <?php

namespace AppCollections;

use DatePeriod;
use IlluminateDatabaseEloquentCollection;
 
class ExampleCollection extends Collection
{
    public function withDefaults()
    {
        $date = $this->sortBy('day')[0]->day->firstOfYear();
        $days = array_map(function($day) {
            return $day->format('Y-m-d');
        }, [...new DatePeriod("R11/{$date->toIso8601ZuluString()}/P1D")]); // e.g. 2021-01-01T00:00:00Z
        $collection = array_fill_keys(array_fill_keys($days, []));
        foreach($this as $item) {
            $collection[$item->day->format('Y-m-d')] = $item;
        }
        return collect($collection);
    }
}

 
  1. Внутри вашей ExampleTable модели добавьте это:
     public function newCollection(array $models = [])
    {   
        return new ExampleCollection($models);
    }
 
  1. Используйте его вот так:
 $results = ExampleTable::select(
    DB::raw("DATE_FORMAT(created_at,'%Y-%M-%d') as day"),
    DB::raw('sum(result) as sumAll')
)
->whereMonth("created_at", '05')
->whereYear("created_at", '2021')
->groupBy('day')
->get()
->withDefaults();
 

Комментарии:

1. спасибо, это замечательно. Я обнаружил ошибку в функции withDefault, но это не проблема, я изменяю это.

2. Рад помочь 🙂 Где вы нашли ошибку? Я сейчас же исправлю свой ответ