Laravel Eloquent get results grouped by days

Viewed 145892

I currently have a table of page_views that records one row for each time a visitor accesses a page, recording the user's ip/id and the id of the page itself. I should add that the created_at column is of type: timestamp, so it includes the hours/minutes/seconds. When I try groupBy queries, it does not group same days together because of the seconds difference.

created_at         page_id       user_id
==========         =======       =======
10-11-2013            3            1
10-12 2013            5            5
10-13 2013            5            2
10-13 2013            3            4
  ...                ...          ...

I'd like to get results based on views/day, so I can get something like:

  date          views
  ====          =====
10-11-2013       15
10-12 2013       45
  ...            ...

I'm thinking I'll need to dig into DB::raw() queries to achieve this, but any insight would help greatly, thanks

Edit: Added clarification of created_at format.

16 Answers

You can filter the results based on formatted date using mysql (See here for Mysql/Mariadb help) and use something like this in laravel-5.4:

Model::selectRaw("COUNT(*) views, DATE_FORMAT(created_at, '%Y %m %e') date")
    ->groupBy('date')
    ->get();

Like most database problems, they should be solved by using the database.

Storing the data you want to group by and using indexes you can achieve an efficient and clear method to solve this problem.

Create the migration

    $table->tinyInteger('activity_year')->unsigned()->index();
    $table->smallInteger('activity_day_of_year')->unsigned()->index();

Update the Model

<?php

namespace App\Models;

  use DB;
  use Carbon\Carbon;
  use Illuminate\Database\Eloquent\Model;

  class PageView extends Model
  {
  public function scopePerDay($query){

     $query->groupBy('activity_year');
     $query->groupBy('activity_day_of_year');

     return $query;

  }

  public function setUpdatedAt($value)
  {   
    $date = Carbon::now();

    $this->activity_year = (int)$date->format('y');
    $this->activity_day_of_year = $date->dayOfYear;

    return parent::setUpdatedAt($value);
 }

Usage

   $viewsPerDay = PageView::perDay()->get();
PageView::select('id','title', DB::raw('DATE(created_at) as date'))
          ->get()
          ->groupBy('date');

Using Laravel 4.2 without Carbon

Here's how I grab the recent ten days and count each row with same day created_at timestamp.

$q = Spins::orderBy('created_at', 'desc')
->groupBy(DB::raw("DATE_FORMAT(created_at, '%Y-%m-%d')"))
->take(10)
->get(array(
      DB::raw('Date(created_at) as date'),
      DB::raw('COUNT(*) as "views"')
  ));


foreach ($q as $day) {

  echo $day->date. " Views: " . $day->views.'<br>';

}

Hope this helps

You could also solve this problem in following way:

$totalView =  View::select(DB::raw('Date(read_at) as date'), DB::raw('count(*) as Views'))
        ->groupBy(DB::raw('Date(read_at)'))
        ->orderBy(DB::raw('Date(read_at)'))
        ->get();

this way work properly and I used it in many projects! for example I get data of views the last 30 days:

$viewsData = DB::table('page_views')
    ->where('page_id', $page->id)
    ->whereDate('created_at', '>=', now()->subDays(30))
    ->select(DB::raw('DATE(created_at) as data'), DB::raw('count(*) as views'))
    ->groupBy('date')
    ->get();

If you want to get the number of views based on different IPs, you can use the DISTINCT like below :

$viewsData = DB::table('page_views')
    ->where('page_id', $page->id)
    ->whereDate('created_at', '>=', now()->subDays(30))
    ->select(DB::raw('DATE(created_at) as data'), DB::raw('count(DISTINCT user_ip) as visitors'))
    ->groupBy('date')
    ->get();

You can easily customize it by manipulating the columns name

you can use the following code to group them by the date, since you have to parse both in the selection query and in the groupBy method:

$counts = DB::table('page_views')->select(DB::raw('DATE(created_at) as created_at'), DB::raw('COUNT(*) as views'))->
              groupBy(DB::raw('DATE(created_at)'))->
              get();
Related