Scheduling Notifications is as easy as creating a new entry in the scheduled_notifications
table. We'll do that in our Controller:
Modifying the Controller
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 store(StoreBookingRequest $request): RedirectResponse { $booking = $request->user()->bookings()->create([ 'start' => fromUserDateTime($request->validated('start'), $request->user()), 'end' => fromUserDateTime($request->validated('end'), $request->user()), ]); $startTime = CarbonImmutable::parse(toUserDateTime($booking->start, $booking->user), $booking->user->timezone); // 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, ]); } return redirect()->route('booking.index'); } // ... public function update(UpdateBookingRequest $request, Booking $booking): RedirectResponse { $booking->update([ 'start' => fromUserDateTime($request->validated('start'), $request->user()), 'end' => fromUserDateTime($request->validated('end'), $request->user()), ]); $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 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, ]); } 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) ->delete(); return redirect()->route('booking.index'); }}
This is it, now if we create a new booking, we will have a scheduled Notification for 1H before the booking starts:
Next up, we will send the Notifications out!
Haveing a little dificalty hear In the github laravel-timezones-course-lesson-3-SendingScheduledNotifications
The above $startTime = CarbonImmutable::parse(toUserDateTime($booking->start, $booking->user), $booking->user->timezone); is in the Models\Booking.php file not the BookingController.php as you have it hear
Content of the BookingController.php file in the GitHub file I downlowed:
Could you rephrase the issue you are facing here? Not sure I understood.
tryed to get the copy of the gethub repository I downloaded to work however I am getting errors dealing with the phpunit file:
Installing phpunit/php-code-coverage (10.1.2): Cloning db1497ec8d from cache db1497ec8dd382e82c962f7abbe0320e4882ee4e is gone (history was rewritten?) Install of phpunit/php-code-coverage failed
Installing phpunit/phpunit (10.2.1): Cloning 599b332943 from cache 599b33294350e8f51163119d5670512f98b0490d is gone (history was rewritten?) Install of phpunit/phpunit failed
Failed to execute git checkout db1497ec8dd382e82c962f7abbe0320e4882ee4e -- && git reset --hard db1497ec8dd382e82
c962f7abbe0320e4882ee4e --
HEAD is now at db1497ec Prepare release
error: unable to create file tests/_files/Report/HTML/CoverageForClassWithAnonymousFunction/source_with_class_an
d_anonymous_function.php.html: Filename too long
ate phpunit/php-code-coverage" to resolve this.
the system tryed to update to Laravel 10.13.2 the latest out as of this time. 6/6/2023 4:14 PM
Help !! ??
I'm not sure why you are getting this, but it seems like you are trying to run phpunit with code coverage. Don't use that flag and it should work just fine.
I was able to re-download from the GitHub repository and it works just fine. I even got the version I am creating to pass the phpUnit test all went well up to this point. Above you have us entering what I find in the class Booking extends Model of the download and not in the Booking Controller where you have us putting it. Even then they are not the same Hear you have: 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 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, ]); } Return redirect()->route(‘booking.index’); }
And on the GitHub repository you have
public function createReminderNotifications(Booking $booking, CarbonImmutable $startTime): void { // 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' => 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' => 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' => 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' => CLASS, 'sent' => false, 'processing' => false, 'scheduled_at' => $startingTime, 'sent_at' => null, 'tries' => 0, ]); } }
Remember this is in the class Booking extends Model
Not the class BookingController extends Controller; hear you have:
public function store(StoreBookingRequest $request): RedirectResponse { $booking = $request->user()->bookings()->create([ 'start' => fromUserDateTime($request->validated('start'), $request->user()), 'end' => fromUserDateTime($request->validated('end'), $request->user()), ]);
What should I do? keep following the GitHub download that I know works! Or you from hear?
There's so much written here but... I understood that the tutorial has skipped these lines:
So I've added that. If There's anything else I've missed - please correct the formatting and I'll take a look!
We are using the
toUserDateTime
function to transform the start date from UTC to user timezone (in your case, it would be UTC + 7). This is required as we need to have the notification specifically for that users timezone.As for why
$data['start']
is UTC - that's because it does not do automatic transformation. You could enable it with cast on your model to tell that it should be aCarbon
instance, but that could bring some issues at a later point. So I like to keep them as strings and mutate manually (easier to debug) :)I see, I think we can change the code from
$startTime = CarbonImmutable::parse(toUserDateTime($booking->start, $booking->user), $booking->user->timezone);
to $startTime = CarbonImmutable::parse($request->start). As I tested it, it produce the same resultCould be! there's many ways to write the same thing!
Class "App\Models\ScheduledNotification" not found while there is code written in \app\Models\User
use App\Models\ScheduledNotification;
public function scheduledNotifications(): HasMany { return $this->hasMany(ScheduledNotification::class); }