Group Entries By Day: Collection groupBy() With CallBack Function

Quite often we need to list the table of data not just entry by entry, but grouped by date. Sports games by date, registered users by date etc. Let’s see how to do it quickly in Laravel.

This is what we’re gonna create in this mini-demo:

As you can see, it’s a list of users grouped by their register date (or, to be precise, users.created_at column).

First, I’ve seeded 50 fake users, by specifying their created_at some time in the past, randomly over 30 days, here’s database/factories/UserFactory.php:

$factory->define(App\User::class, function (Faker $faker) {
    return [
        'name' => $faker->name,
        'email' => $faker->unique()->safeEmail,
        'email_verified_at' => now(),
        'password' => '$2y$10$TKh8H1.PfQx37YgCzwiKb.KjNyWgaHb9cbcoQgdIVFlYg7B77UdFm', // secret
        'remember_token' => str_random(10),
        'created_at' => now()->subDays(rand(1,30)),
    ];
});

Next, seeded 50 users in database/seeds/DatabaseSeeder.php:

class DatabaseSeeder extends Seeder
{
    public function run()
    {
        factory(\App\User::class, 50)->create();
    }
}

Now, let’s get to today’s topic – how to group the data. Here’s the controller code:

use App\User;

class UserController extends Controller
{

    public function index()
    {
        $users = User::orderBy('created_at')->get()->groupBy(function($item) {
            return $item->created_at->format('Y-m-d');
        });

        return view('users', compact('users'));
    }

}

A few notices here:

  • Function groupBy() is called AFTER the get(), it is a Collection function and not Eloquent. If you do groupBy() before get(), it wouldn’t work, cause Eloquent groupBy() doesn’t accept callback parameter.
  • I’m doing group by formatted column $item->created_at->format(‘Y-m-d’), it will work only on created_at/updated_at column that are automatically transformed to Carbon objects. If you want to group by other date/time fields, you should use Carbon, like Carbon::createFromFormat(‘Y-m-d H:i:s’, $item->register_time)

Now, here’s the code for the table in our resources/views/users.blade.php file:

<table class="table">
    @foreach ($users as $day => $users_list)
        <tr>
            <th colspan="3"
                style="background-color: #F7F7F7">{{ $day }}: {{ $users_list->count() }} users</th>
        </tr>
        @foreach ($users_list as $user)
            <tr>
                <td>{{ $user->name }}</td>
                <td>{{ $user->email }}</td>
                <td>{{ $user->created_at }}</td>
            </tr>
        @endforeach
    @endforeach
</table>

As you can see, we can still call $users_list->count() on the result, too. Pretty convenient.

So, that’s it. In short, you can do groupBy() with callback function, after getting all query results.

Read more about Collections groupBy() method here.

Like our articles?
Check out our Laravel online courses!

LEAVE A REPLY

Please enter your comment!
Please enter your name here