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

Ady_Gould's avatar

Grouping notifications per user with sending variations

I am trying to extend the good old Chirper bootcamp example for my students.

As we know, every time we chirp an email is sent to every user... this will be a big scalability problem when we get hundreds, thousands or millions of users.

For example:

  • Pheonix sends 15 chirps
  • Callum sends 25 chirps
  • Deryn sends 9 chirps

This means that without grouping:

  • Callum will get 24 notification emails
  • Deryn will have 40 notification emails
  • Pheonix will have 34 notification emails

For a total of 98 emails sent.

What I would like to do is:

  • group notifications per user into sets of m notifications in ONE email per user
  • send unsent notifications (less than m) at the end of the day

So, if we have 15 users, who send a total of 45 chirps in the day, then an email is sent to the users with the notifications about other user chirps once we get to m new chirps for the user being sent to.

At the end of the day, the remaining notification would then be batched up and sent in an end of day email.

This would mean, if a group contained (m) 25 notices:

  • Callum will get 1 notification email, sent at the end of the day
  • Deryn will have 2 notification emails, one sent after 25, the other at the end of the day
  • Pheonix will have 2 notification emails, one sent after 25, the other at the end of the day

I've been hunting for a tutorial, tried AI assisted coding, and other ways, but without much success.

Any guidance would be appreciated, and credited in the notes being created with the Chirper bootcamp as the basis.


FYI: Chirper - the Laravel (<v12) BootCamp

I've 'resurrected' the bootcamp and have been extending it, not that my code is brilliant, but its about concepts over perfection at the moment. https://github.com/adygcode/chirper-2025-s1

0 likes
2 replies
LaryAI's avatar

Great question! What you’re describing is a classic notification batching pattern, and it’s a great way to improve scalability and user experience.

Here’s a step-by-step approach to implement this in Laravel (works for Laravel 10/11/12):


1. Database Design

You’ll need a notifications table (Laravel provides this if you use notifications), but you’ll want to add a sent (boolean) and maybe a batch_id (nullable) column to track which notifications have been included in a batch email.

Migration Example:

php artisan make:migration add_sent_and_batch_id_to_notifications_table --table=notifications
// database/migrations/xxxx_xx_xx_add_sent_and_batch_id_to_notifications_table.php

public function up()
{
    Schema::table('notifications', function (Blueprint $table) {
        $table->boolean('sent')->default(false);
        $table->string('batch_id')->nullable();
    });
}

2. Storing Notifications

When a chirp is created, store a notification for each user as usual, but do not send an email immediately.

// In your ChirpCreated event or observer

foreach (User::where('id', '!=', $chirp->user_id)->get() as $user) {
    $user->notifications()->create([
        'type' => ChirpNotification::class,
        'data' => [
            'chirp_id' => $chirp->id,
            // ...other data
        ],
        'sent' => false,
    ]);
}

3. Batching Logic

You need a scheduled job (artisan command or queued job) that:

  • For each user, finds all unsent notifications.
  • If the count >= m, send an email with up to m notifications, mark them as sent (and optionally assign a batch_id).
  • At the end of the day, send any remaining unsent notifications.

Example Command

php artisan make:command SendBatchedNotifications

4. End-of-Day Batch

Schedule another command (or add logic to the above) to run at, say, 23:55, that sends all remaining unsent notifications for each user.

// In the same command, add:
foreach ($users as $user) {
    $unsent = $user->notifications()->where('sent', false)->get();

    if ($unsent->count() > 0) {
        $this->sendBatch($user, $unsent);
    }
}

5. Scheduling

Add to app/Console/Kernel.php:

protected function schedule(Schedule $schedule)
{
    // Run every 10 minutes for regular batching
    $schedule->command('notifications:batch')->everyTenMinutes();

    // Run at end of day for leftovers
    $schedule->command('notifications:batch --end-of-day')->dailyAt('23:55');
}

6. Mail Example

Create a mailable to display the batch:

php artisan make:mail BatchedChirpNotificationMail
// app/Mail/BatchedChirpNotificationMail.php

public function __construct($notifications)
{
    $this->notifications = $notifications;
}

public function build()
{
    return $this->view('emails.batched_chirps')
        ->with(['notifications' => $this->notifications]);
}

7. Summary

  • Store notifications as usual, but don’t email immediately.
  • Use a scheduled command to batch and send notifications per user.
  • At the end of the day, send any remaining notifications.
  • Mark notifications as sent to avoid duplicates.

Extra Tips

  • You can use Laravel’s built-in notification system, but you’ll need to override the default toMail method to prevent immediate sending.
  • Consider using queues for sending emails to avoid blocking.
  • Make the batch size configurable.

Let me know if you need a full code example for any part of this!

Ady_Gould's avatar

@LaryAI

Taking one item that does not seem to be covered - the fact that each user gets different notifications as:

  • they do not receive their own notifications
  • not all notifications will send at same time (due to above)

This would indicate that the notifications table would need to track the user for each notification batch.

How to accommodate this?

Please or to participate in this conversation.