Overview
The appointment system manages scheduling between nutritionists and patients, including availability management, state tracking, and automated reminders.
Appointments are the core of NutriFit’s workflow, connecting patients with nutritionists for clinical consultations.
Appointment Model
The Appointment model tracks all scheduling data and relationships.
app/Models/Appointment.php
class Appointment extends Model
{
protected $fillable = [
'appointment_state_id' ,
'paciente_id' ,
'nutricionista_id' ,
'start_time' ,
'end_time' ,
'reason' ,
'appointment_type' ,
'price' ,
'notes' ,
];
protected $casts = [
'start_time' => 'datetime' ,
'end_time' => 'datetime' ,
'price' => 'decimal:2' ,
];
}
Relationships
Patient belongsTo User (role: paciente)
Nutritionist belongsTo User (role: nutricionista)
State belongsTo AppointmentState
Attention hasOne Attention (clinical record)
Appointment States
Appointments progress through different states during their lifecycle:
Pendiente (Pending)
Initial state when appointment is created
Confirmada (Confirmed)
Appointment confirmed by nutritionist
Completada (Completed)
Clinical attention has been recorded
Cancelada (Cancelled)
Cancelled by patient or nutritionist
Vencida (Expired)
Automatically set when appointment time passes without completion
State Management
app/Models/AppointmentState.php
class AppointmentState extends Model
{
protected $fillable = [
'name' ,
'description' ,
];
}
Scheduling Features
Patient Booking
Patients can schedule appointments through the booking interface:
Route :: middleware ([ 'auth' , 'verified' , 'role:paciente' , 'password.changed' ])
-> prefix ( 'paciente' )
-> name ( 'paciente.' )
-> group ( function () {
Route :: get ( '/agendar' , [ PacienteController :: class , 'showBooking' ])
-> name ( 'booking.index' );
Route :: get ( '/agendar/{nutricionista}' , [ PacienteController :: class , 'selectSchedule' ])
-> name ( 'booking.schedule' );
Route :: post ( '/agendar/{nutricionista}' , [ PacienteController :: class , 'storeAppointment' ])
-> name ( 'booking.store' );
});
Select Nutritionist
Patient browses available nutritionists
View Availability
System shows nutritionist’s available time slots
Choose Time
Patient selects date and time
Confirm Booking
Appointment is created in “pendiente” state
Notifications Sent
Both patient and nutritionist receive email confirmations
Nutritionist Scheduling
Nutritionists can assign appointments to existing patients:
Route :: middleware ([ 'auth' , 'verified' , 'role:nutricionista' ])
-> prefix ( 'nutricionista' )
-> name ( 'nutricionista.' )
-> group ( function () {
// Asignar citas a pacientes
Route :: get ( '/citas/asignar' , [ NutricionistaController :: class , 'createAppointment' ])
-> name ( 'appointments.create' );
Route :: get ( '/citas/asignar/{paciente}/horarios' ,
[ NutricionistaController :: class , 'getAvailableSchedules' ])
-> name ( 'appointments.schedules' );
Route :: post ( '/citas/asignar' , [ NutricionistaController :: class , 'storeAppointment' ])
-> name ( 'appointments.store' );
});
Availability Management
Nutritionists manage their availability through the schedule interface.
Schedule Configuration
// Gestión de horarios
Route :: get ( '/horarios' , [ NutricionistaController :: class , 'schedules' ])
-> name ( 'schedules.index' );
Route :: post ( '/horarios' , [ NutricionistaController :: class , 'saveSchedules' ])
-> name ( 'schedules.save' );
Nutritionists can:
Set working hours for each day of the week
Define appointment duration
Block specific time slots
Set recurring availability patterns
Appointment Operations
Viewing Appointments
Patient View
Nutritionist View
Admin View
Route :: get ( '/citas' , [ PacienteController :: class , 'appointments' ])
-> name ( 'appointments.index' );
Route :: get ( '/citas/{appointment}' , [ PacienteController :: class , 'showAppointment' ])
-> name ( 'appointments.show' );
Route :: get ( '/citas' , [ NutricionistaController :: class , 'appointments' ])
-> name ( 'appointments.index' );
Route :: get ( '/appointments/{appointment}' , [ NutricionistaController :: class , 'showAppointment' ])
-> name ( 'appointments.show' );
Route :: get ( '/appointments' , [ AdminController :: class , 'appointments' ])
-> name ( 'appointments.index' );
Route :: get ( '/appointments/{appointment}' , [ AdminController :: class , 'showAppointment' ])
-> name ( 'appointments.show' );
Cancellation
Both patients and nutritionists can cancel appointments:
// Patient cancellation
Route :: post ( '/citas/{appointment}/cancelar' , [ PacienteController :: class , 'cancelAppointment' ])
-> name ( 'appointments.cancel' );
// Nutritionist cancellation
Route :: post ( '/appointments/{appointment}/cancel' , [ NutricionistaController :: class , 'cancelAppointment' ])
-> name ( 'appointments.cancel' );
When an appointment is cancelled, both parties receive notification emails (AppointmentCancelledByPatient or AppointmentCancelledByNutricionista).
Rescheduling
Nutritionists can reschedule appointments:
Route :: get ( '/appointments/{appointment}/reschedule' ,
[ NutricionistaController :: class , 'rescheduleForm' ])
-> name ( 'appointments.reschedule' );
Route :: post ( '/appointments/{appointment}/reschedule' ,
[ NutricionistaController :: class , 'rescheduleAppointment' ])
-> name ( 'appointments.reschedule.store' );
Automated Reminders
Reminder Command
The system includes an automated reminder command that runs daily:
app/Console/Commands/SendAppointmentReminders.php
class SendAppointmentReminders extends Command
{
protected $signature = 'appointments:send-reminders' ;
protected $description = 'Envía recordatorios de citas que serán mañana' ;
public function handle ()
{
$tomorrow = Carbon :: tomorrow () -> toDateString ();
$appointments = Appointment :: whereDate ( 'start_time' , $tomorrow )
-> whereHas ( 'appointmentState' , function ( $query ) {
$query -> whereIn ( 'name' , [ 'confirmada' , 'pendiente' ]);
})
-> with ([ 'paciente' , 'nutricionista' ])
-> get ();
foreach ( $appointments as $appointment ) {
// Notificar al paciente
$appointment -> paciente -> notify (
new AppointmentReminderNotification ( $appointment )
);
// Notificar al nutricionista
$appointment -> nutricionista -> notify (
new AppointmentReminderNotification ( $appointment )
);
}
}
}
Scheduling the Command
Add to app/Console/Kernel.php:
protected function schedule ( Schedule $schedule )
{
$schedule -> command ( 'appointments:send-reminders' )
-> dailyAt ( '09:00' );
}
Expired Appointments
The system automatically marks expired appointments:
app/Models/Appointment.php
public static function markExpiredAppointments () : int
{
$vencidaState = AppointmentState :: where ( 'name' , 'vencida' ) -> first ();
if ( ! $vencidaState ) {
return 0 ;
}
// Actualizar citas que ya pasaron y aún están pendientes
return self :: whereHas ( 'appointmentState' , fn ( $q ) => $q -> where ( 'name' , 'pendiente' ))
-> where ( 'end_time' , '<' , now ())
-> update ([ 'appointment_state_id' => $vencidaState -> id ]);
}
public function isExpired () : bool
{
return $this -> end_time < now () && $this -> appointmentState -> name === 'pendiente' ;
}
Run this periodically using a scheduled command to keep appointment states accurate.
Notifications
The appointment system triggers various notifications:
AppointmentCreatedNotification
Sent to nutritionist when patient books an appointment
AppointmentCreatedForPatientNotification
Sent to patient confirming their booking
AppointmentConfirmedForPatient
Sent when nutritionist confirms the appointment
AppointmentReminderNotification
Sent 24 hours before appointment to both parties
AppointmentRescheduledNotification
Sent when appointment time is changed
AppointmentCancelledByPatient
Sent to nutritionist when patient cancels
AppointmentCancelledByNutricionista
Sent to patient when nutritionist cancels
Integration with Clinical Records
Appointments link to clinical attention records:
// After appointment, nutritionist creates attention record
Route :: get ( '/citas/{appointment}/atender' , [ AttentionController :: class , 'create' ])
-> name ( 'attentions.create' );
Route :: post ( '/citas/{appointment}/atender' , [ AttentionController :: class , 'store' ])
-> name ( 'attentions.store' );
When attention is recorded, appointment state changes to “completada”.
Admin Management
Administrators have full oversight:
Route :: middleware ([ 'auth' , 'verified' , 'role:administrador' ])
-> prefix ( 'administrador' )
-> name ( 'admin.' )
-> group ( function () {
Route :: get ( '/appointments' , [ AdminController :: class , 'appointments' ])
-> name ( 'appointments.index' );
Route :: get ( '/appointments/{appointment}' , [ AdminController :: class , 'showAppointment' ])
-> name ( 'appointments.show' );
Route :: post ( '/appointments/{appointment}/cancel' ,
[ AdminController :: class , 'cancelAppointment' ])
-> name ( 'appointments.cancel' );
});
Clinical Records Learn about attention records and patient data
Notifications Explore appointment notification system
User Roles Understand role-based permissions