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:
| Event | Payload | Dispatched when |
|---|---|---|
AppointmentCreated | $appointment | An appointment row is inserted. |
AppointmentUpdated | $appointment | An appointment is saved with changes — including reschedules and status transitions. |
AppointmentRescheduled | $appointment, $previousStartsAt | reschedule() moves an appointment. |
AppointmentStatusChanged | $appointment, $from, $to | A status transition happens — directly or via the approval sync. |
ParticipantCreated | $participant | A participant is added for the first time. |
ParticipantUpdated | $participant | A participant row is saved with changes — or a removed participant is added again and its row restored. |
ParticipantDeleted | $participant | A 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
| Exception | Thrown when | Carries |
|---|---|---|
InvalidStatusTransitionException | A status transition the state machine doesn’t allow. | $from, $to |
SchedulingConflictException | Creating, rescheduling or adding a participant overlaps a participant’s booking while conflict prevention is on. | $conflicts — Collection of Appointment |
DuplicateParticipantException | The same model is listed twice on a new appointment, or participants()->add() gets a model that already takes part. | — |
ParticipantNotFoundException | participants()->remove() gets a model that doesn’t take part, or a participant row of another appointment. | — |
InvalidScheduleException | A 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. | — |
InvalidRecurrenceException | A RecurrenceData with an interval or count below one, or a weekday outside 1–7. | — |
CannotReviewAppointmentException | review() for an unverified author while reviews.require_verified_attendance is on. | — |
AppointmentsException | Abstract 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 cryptoBy 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.