Back to Course |
Laravel User Timezones Project: Convert, Display, Send Notifications

Adding More Notification Classes

In this lesson, we will look at how we can add more than one Notification to be sent out to the user. We will create a few more Notifications and schedule them to be sent out at different times.


Creating the Notifications

Currently, we only have a Notification that is sent 1 hour before the booking, but we need new ones that are sent at:

  • 2 hours before the booking starts
  • 5 minutes before the booking starts
  • at the time the booking starts

We can get this quickly done by using the php artisan make:notification command:

php artisan make:notification BookingReminder2H
php artisan make:notification BookingReminder5Min
php artisan make:notification BookingStartedNotification

And then we will edit all the Notifications to accept the Booking Model as a parameter:

app/Notifications/BookingReminder2H.php

use App\Models\Booking;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
 
class BookingReminder2H extends Notification
{
public function __construct(protected Booking $booking)
{
}
 
public function via($notifiable): array
{
return ['mail'];
}
 
public function toMail($notifiable): MailMessage
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
 
public function toArray($notifiable): array
{
return [];
}
}

app/Notifications/BookingReminder5MIN.php

use App\Models\Booking;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
 
class BookingReminder5MIN extends Notification
{
public function __construct(protected Booking $booking)
{
}
 
public function via($notifiable): array
{
return ['mail'];
}
 
public function toMail($notifiable): MailMessage
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
 
public function toArray($notifiable): array
{
return [];
}
}

app/Notifications/BookingStartedNotification.php

use App\Models\Booking;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
 
class BookingStartedNotification extends Notification
{
public function __construct(protected Booking $booking)
{
}
 
public function via($notifiable): array
{
return ['mail'];
}
 
public function toMail($notifiable): MailMessage
{
return (new MailMessage)
->line('The introduction to the notification.')
->action('Notification Action', url('/'))
->line('Thank you for using our application!');
}
 
public function toArray($notifiable): array
{
return [];
}
}

Our basic Notifications are ready to go. Next up are more preparations for scheduling them.


Scheduling the Notifications

Next is the actual scheduling part of the Notifications. We will modify our Controller to schedule the Notifications:

app/Http/Controllers/BookingController.php:

use App\Http\Requests\StoreBookingRequest;
use App\Http\Requests\UpdateBookingRequest;
use App\Models\Booking;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
 
class BookingController extends Controller
{
public function index()
{
$bookings = Booking::query()
->with(['user'])
->get();
 
return view('bookings.index', compact('bookings'));
}
 
public function create()
{
return view('bookings.create');
}
 
public function store(StoreBookingRequest $request): RedirectResponse
{
$booking = $request->user()->bookings()->create($request->validated());
$startTime = CarbonImmutable::parse(toUserDateTime($booking->start, $booking->user), $booking->user->timezone);
 
// Schedule 2H reminder
$twoHoursTime = fromUserDateTime($startTime->subHours(2), $booking->user);
if (now('UTC')->lessThan($twoHoursTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingReminder2H::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $twoHoursTime,
'sent_at' => null,
'tries' => 0,
]);
}
// Schedule 1H reminder
$oneHourTime = fromUserDateTime($startTime->subHour(), $booking->user);
if (now('UTC')->lessThan($oneHourTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingReminder1H::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $oneHourTime,
'sent_at' => null,
'tries' => 0,
]);
}
// Schedule 5 min reminder
$fiveMinutesTime = fromUserDateTime($startTime->subMinutes(5), $booking->user);
if (now('UTC')->lessThan($fiveMinutesTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingReminder5MIN::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $fiveMinutesTime,
'sent_at' => null,
'tries' => 0,
]);
}
// Schedule started reminder
$startingTime = fromUserDateTime($startTime, $booking->user);
if (now('UTC')->lessThan($startingTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingStartedNotification::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $startingTime,
'sent_at' => null,
'tries' => 0,
]);
}
 
return redirect()->route('booking.index');
}
 
public function edit(Booking $booking)
{
return view('bookings.edit', compact('booking'));
}
 
public function update(UpdateBookingRequest $request, Booking $booking): RedirectResponse
{
$booking->update($request->validated());
 
$startTime = CarbonImmutable::parse(toUserDateTime($booking->start, $booking->user), $booking->user->timezone);
 
$hasScheduledNotifications = ScheduledNotification::query()
->where('notifiable_id', $booking->id)
->where('notifiable_type', Booking::class)
->where('user_id', $booking->user_id)
->exists();
 
// First we need to check if there are any already scheduled notifications
if ($hasScheduledNotifications) {
// Then in this example, we simply delete them. You can however update them if you want.
$booking->scheduledNotifications()
->where('user_id', $booking->user_id)
->delete();
}
 
// Since we are clearing the scheduled notifications, we need to create them again for the new date
 
// Schedule 2H reminder
$twoHoursTime = fromUserDateTime($startTime->subHours(2), $booking->user);
if (now('UTC')->lessThan($twoHoursTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingReminder2H::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $twoHoursTime,
'sent_at' => null,
'tries' => 0,
]);
}
// Schedule 1H reminder
$oneHourTime = fromUserDateTime($startTime->subHour(), $booking->user);
if (now('UTC')->lessThan($oneHourTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingReminder1H::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $oneHourTime,
'sent_at' => null,
'tries' => 0,
]);
}
// Schedule 5 min reminder
$fiveMinutesTime = fromUserDateTime($startTime->subMinutes(5), $booking->user);
if (now('UTC')->lessThan($fiveMinutesTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingReminder5MIN::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $fiveMinutesTime,
'sent_at' => null,
'tries' => 0,
]);
}
// Schedule started reminder
$startingTime = fromUserDateTime($startTime, $booking->user);
if (now('UTC')->lessThan($startingTime)) {
$booking->user->scheduledNotifications()->create([
'notification_class' => BookingStartedNotification::class,
'notifiable_id' => $booking->id,
'notifiable_type' => Booking::class,
'sent' => false,
'processing' => false,
'scheduled_at' => $startingTime,
'sent_at' => null,
'tries' => 0,
]);
}
 
return redirect()->route('booking.index');
}
 
public function destroy(Request $request, Booking $booking): RedirectResponse
{
abort_unless($booking->user_id === $request->user()->id, 404);
 
$booking->delete();
 
$booking->scheduledNotifications()
->where('user_id', $booking->user_id)
->where('user_id', $booking->user_id)
->delete();
 
return redirect()->route('booking.index');
}
}

Now we will have 4 Notifications scheduled for us:


Sending them with a Scheduler

Now that we have our scheduled Notifications in the database, we need to send them. For this, we'll modify our Job:

app/Jobs/ProcessNotificationJob.php

use App\Notifications\BookingReminder1H;
use App\Notifications\BookingReminder2H;
use App\Notifications\BookingReminder5MIN;
use App\Notifications\BookingStartedNotification;
 
// ...
 
public function handle(): void
{
if ($this->notification->sent || $this->notification->tries >= config('app.notificationAttemptAmount')) {
return;
}
if (!$this->notification->notifiable) {
// Makes sure that the notifiable is still available
$this->fail();
return;
}
try {
switch ($this->notification->notification_class) {
case BookingReminder1H::class:
$this->notification->user->notify(new BookingReminder1H($this->notification->notifiable));
break;
case BookingReminder2H::class:
$this->notification->user->notify(new BookingReminder2H($this->notification->notifiable));
break;
case BookingReminder5MIN::class:
$this->notification->user->notify(new BookingReminder5MIN($this->notification->notifiable));
break;
case BookingStartedNotification::class:
$this->notification->user->notify(new BookingStartedNotification($this->notification->notifiable));
break;
}
 
$this->notification->update(['processing' => false, 'sent' => true, 'sent_at' => now()]);
} catch (Exception $exception) {
$this->fail($exception);
}
}

As you can see, we just need to add more cases to our switch statement. And once that is done - our Notifications are going to be sent out.