Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

CookieMonster's avatar

How to query records count based on days?

I have a table of Forms where people can sign up and their details are stored in the table. I am trying to display the statistics on how many number of people sign up on the particular day. For instance, my blade view should look like this:

Date                       Number of Sign Up
25 Feb 2022                     1
24 Feb 2022                     2
23 Feb 2022                     0
22 Feb 2022                     0
21 Feb 2022                     5
20 Feb 2022                     0
19 Feb 2022                     0
18 Feb 2022                     0
TOTAL                           8

It displays the current day up until 7 days before so it will be dynamic.

Now I have to write some complicated query for the data in my controller:

 $sign_ups = DB::table('forms')
             ->select('created_at', DB::raw('count(*) as sign_ups'))
             ->whereBetween('created_at', [date('Y-m-d', strtotime('-7 days')), date('Y-m-d')])
             ->groupBy('created_at')
             ->get();

Or a more Eloquent-ish query:

$sign_ups = Form::whereBetween('created_at', [date('Y-m-d', strtotime('-7 days')), date('Y-m-d')])
                            ->groupBy('created_at')
                            ->orderBy('created_at', 'DESC')
                            ->get(array(
                                DB::raw('Date(created_at) as date'),
                                DB::raw('COUNT(*) as "sign_ups"')
                            ));

The only issue here it does not distinct from the day but the time as well so every count returns as 1.

1st element
"date" => "2022-02-24"
 "sign_ups" => 1

2nd element
"date" => "2022-02-24"
 "sign_ups" => 1

3rd element
"date" => "2022-02-23"
 "sign_ups" => 1

........

Another issue is that some days do not have any record so how would I populate it on the blade without record. How do I go about tackling this issue?

0 likes
30 replies
Sinnbeck's avatar

Sounds like you want to group by date?

->groupByRaw('DATE(created_at)') 
Sinnbeck's avatar

@cookiemonster

$sign_ups = DB::table('forms')
             ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as sign_ups'))
             ->whereBetween('created_at', [date('Y-m-d', strtotime('-7 days')), date('Y-m-d')])
             ->groupByRaw('DATE(created_at)') 
             ->get(); 
1 like
Sinnbeck's avatar

@CookieMonster almost the same

$sign_ups = Form::query()
             ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as sign_ups'))
             ->whereBetween('created_at', [date('Y-m-d', strtotime('-7 days')), date('Y-m-d')])
             ->groupByRaw('DATE(created_at)') 
             ->get();  
CookieMonster's avatar

@Sinnbeck In your first solution, how do sort it based on latest date?:

  $sign_ups = DB::table('forms')
        ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as count'))
        ->whereBetween('created_at', [date('Y-m-d', strtotime('-7 days')), date('Y-m-d')])
        ->groupByRaw('DATE(created_at)')
        ->latest()
        ->get(); 

This returns Syntax error or access violation: 1055 Expression #1 of ORDER BY clause is not in GROUP BY clause and contains nonaggregated column.

Sinnbeck's avatar
Sinnbeck
Best Answer
Level 102

@CookieMonster ah sorry I missed that. The same

$sign_ups = DB::table('forms')
        ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as count'))
        ->whereBetween('created_at', [date('Y-m-d', strtotime('-7 days')), date('Y-m-d')])
        ->groupByRaw('DATE(created_at)')
        ->orderByRaw('DATE(created_at) desc') 
        ->get();  
2 likes
Sinnbeck's avatar

Let me know if you need help with the second part that you deleted. The trick is to use carbon period

CookieMonster's avatar

@Sinnbeck I thought of building an array with the dates but it would be too complicated.

This is how my view looks like:

<div class="content">
        <div class="container-fluid">
            <table class="table table-bordered">
                <thead>
                  <tr>
                    <th scope="col">Date</th>
                    <th scope="col">Number of sign ups</th>
                  </tr>
                </thead>
                <tbody>
                 @foreach($sign_ups as $sign_up)
                 <tr>
                    <td>{{ $sign_up->date }}</td>
                    <td>{{ $sign_up->count }}</td>     
                  </tr>
                 @endforeach
                </tbody>
              </table>
        </div>
    </div>

For now, it does only shows the collection from $sign_ups. How would you accomplish this?

Sinnbeck's avatar

@CookieMonster I would prepare it first

$sign_ups = DB::table('forms')
        ->select(DB::raw('DATE(created_at) as date'), DB::raw('count(*) as count'))
        ->whereBetween('created_at', [date('Y-m-d', strtotime('-7 days')), date('Y-m-d')])
        ->groupByRaw('DATE(created_at)')
        ->orderByRaw('DATE(created_at) desc') 
        ->get()
        ->pluck('count', 'date');  //notice the pluck here to map it to key->value

$period = CarbonPeriod::create(today()->subDays(7), '1 day', today());
$final = [];

foreach ($period as $date) {
    $date = $date->toDateString();
    $final[] = [
         'date' = $date,
        'count' => $sign_ups[$date] ?? 0,
   ];
}

dd($final);
 
CookieMonster's avatar

@Sinnbeck Think you made typo. Should be:

foreach ($period as $date) {
            $date = $date->toDateString();
            $final[] = [
                'date' => $date,
                'count' => $sign_ups[$date] ?? 0,
        ];
CookieMonster's avatar

@Sinnbeck It resets the order of the date. Do I just use array_reverse($final) to re-sort them to the latest to oldest?

Sinnbeck's avatar

@CookieMonster Just flip the order here

$period = CarbonPeriod::create(today(), '1 day', today()->subDays(7));
CookieMonster's avatar

@Sinnbeck It wouldn't work since die dump returns empty. If I use array_reverse($final) then it can work.

CookieMonster's avatar

@Sinnbeck Perhaps this is another alternative:

array_reverse(CarbonPeriod::create(today()->subDays(7), '1 day', today()))->toArray();

It could be a bit less performant though.

CookieMonster's avatar

@Sinnbeck Just one problem though. Since it is not passing the data to the view as a collection:

   $datas = [];

        foreach ($period as $date) {
            $date = $date->toDateString();
            $datas[] = [
                'date' => $date,
                'count' => $sign_ups[$date] ?? 0,
            ];
        }
        //sort the date based on latest to oldest
        $datas = array_reverse($datas);
       // dd(collect($datas));

View:

 @foreach($datas as $data)
                 <tr>
                    <td>{{ $data->date }}</td>
                    <td>{{ $data->count }}</td>     
                  </tr>
    @endforeach

I cannot access the values without giving a Trying to get property 'date' of non-object error. Tried doing collect($datas) but it wouldn't work as well.

Sinnbeck's avatar

@CookieMonster Yup. It is an array now, not an object

@foreach($datas as $data)
                 <tr>
                    <td>{{ $data['date'] }}</td>
                    <td>{{ $data['count'] }}</td>     
                  </tr>
    @endforeach

or cast to object if thats what you want

        foreach ($period as $date) {
            $date = $date->toDateString();
            $datas[] = (object) [ //here
                'date' => $date,
                'count' => $sign_ups[$date] ?? 0,
            ];
        }
Sinnbeck's avatar

@CookieMonster You still cannot use object syntax then, as it is a collection of arrays. And no need to overcomplicate things ;)

1 like
CookieMonster's avatar

@Sinnbeck I see. Realized I could not use @if(!$datas->isEmpty()) as well but only could use count($datas) when cast to an object.

Please or to participate in this conversation.