NewWe open-sourced 50+ Laravel packages
Custom AI apps, agents and automation — Roundly ConsultingRoundly
All packages
Appointments for Laravel

Events & exceptions

The package fires an event for every change, so confirmations, calendar sync and dashboards stay out of your scheduling code. All live in RoundlyConsulting\Appointments\Events:

EventPayloadDispatched when
AppointmentCreated$appointmentAn appointment row is inserted.
AppointmentUpdated$appointmentAn appointment is saved with changes — including reschedules and status transitions.
AppointmentRescheduled$appointment, $previousStartsAtreschedule() moves an appointment.
AppointmentStatusChanged$appointment, $from, $toA status transition happens — directly or via the approval sync.
ParticipantCreated$participantA participant is added for the first time.
ParticipantUpdated$participantA participant row is saved with changes — or a removed participant is added again and its row restored.
ParticipantDeleted$participantA participant is removed (soft delete) — one event per row.
use Illuminate\Support\Facades\Event;
use RoundlyConsulting\Appointments\Enums\Status;
use RoundlyConsulting\Appointments\Events\AppointmentStatusChanged;

Event::listen(function (AppointmentStatusChanged $event): void {
    if ($event->to === Status::Confirmed) {
        foreach ($event->appointment->participants as $participant) {
            // notify $participant->participant that the booking is confirmed
        }
    }
});

Timing notes

  • Every package event implements ShouldDispatchAfterCommit: inside a database transaction it fires once that commits, and never for a write that rolls back.
  • A booking is written in one transaction, so AppointmentCreated fires after its participants, contacts and approval request are stored — and a refused or failed booking fires nothing.
  • A recurring series is one transaction too: its events fire once the whole series commits, and a series that rolls back fires none.
  • reschedule() and status transitions save the model, so AppointmentUpdated fires alongside AppointmentRescheduled or AppointmentStatusChanged.
  • A status set at creation with withStatus() doesn’t fire AppointmentStatusChanged, and neither does a transition to the current status.

Exceptions

ExceptionThrown whenCarries
InvalidStatusTransitionExceptionA status transition the state machine doesn’t allow.$from, $to
SchedulingConflictExceptionCreating, rescheduling or adding a participant overlaps a participant’s booking while conflict prevention is on.$conflicts — Collection of Appointment
DuplicateParticipantExceptionThe same model is listed twice on a new appointment, or participants()->add() gets a model that already takes part.—
ParticipantNotFoundExceptionparticipants()->remove() gets a model that doesn’t take part, or a participant row of another appointment.—
InvalidScheduleExceptionA duration under one minute, or an end that doesn’t come after the start — from the builder, an AppointmentData or reschedule(). Thrown before anything is written.—
InvalidRecurrenceExceptionA RecurrenceData with an interval or count below one, or a weekday outside 1–7.—
CannotReviewAppointmentExceptionreview() for an unverified author while reviews.require_verified_attendance is on.—
AppointmentsExceptionAbstract base class of all the exceptions above.—

All live in RoundlyConsulting\Appointments\Exceptions and extend AppointmentsException, so one catch covers the package.

Show your open-source love

This package is free and MIT-licensed. If it saves you time, a one-off donation or a Patreon membership keeps it maintained, tested and documented.

More ways to support, including crypto

By donating, you agree to our donation terms.

Want this built into your product?

We integrate our packages into custom Laravel and AI builds. Tell us what you're working on and we'll reply within 48 hours.