phpmysqllaravellaravel-query-builder

Laravel Group Query Result by Day on Month-Year (show all date if not exist date)


I have a table exampleTable :

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

I want result:

+------------+--------+
| 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 |

And this my query:

$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();

But that not show if date not exist.
I want day value with date and result value set 0 if data not exist


Solution

    1. Create a new collection via php artisan make:collection \\App\\Collections\\ExampleCollection
    2. Do the following:
    <?php
    
    namespace App\Collections;
    
    use DatePeriod;
    use Illuminate\Database\Eloquent\Collection;
     
    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. Inside your ExampleTable model add this:
        public function newCollection(array $models = [])
        {   
            return new ExampleCollection($models);
        }
    
    1. Use it like this:
    $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();