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 BookingReminder2Hphp artisan make:notification BookingReminder5Minphp 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.
I can't understand what this is for?
this allows us to switch notification class based on what we saved in database. this allows us to quickly add/remove/replace notification classes
Your reminder classes should simply implement the BookingReminderInterface.
This was not the goal of this lesson :)
We don't want to add complications around things, when it is not the focus of a lesson