Skip to main content

Overview

The PacienteController handles all patient-specific operations in NutriFit. It enables patients to book appointments with nutritionists, view their appointment history, access their clinical records, and manage their profile. Namespace: App\Http\Controllers Middleware: auth, verified, role:paciente, password.changed Route Prefix: /paciente

Dashboard

index()

Displays the patient dashboard with statistics and appointment information.
public function index()
Route: GET /paciente/dashboard Route Name: paciente.dashboard Returns: View with:
  • Appointment statistics (total, completed, pending)
  • Next appointment details
  • Recent appointments (last 10)
  • List of available nutritionists (clinically enabled only)
Side Effects: Automatically marks expired appointments Example Statistics:
[
    'total' => 8,
    'completadas' => 5,
    'pendientes' => 1
]

Appointment Booking

showBooking()

Displays the list of available nutritionists for booking.
public function showBooking()
Route: GET /paciente/agendar Route Name: paciente.booking.index Returns: View with list of clinically enabled nutritionists and their schedule counts

selectSchedule(User $nutricionista)

Displays available time slots for a specific nutritionist.
public function selectSchedule(User $nutricionista)
Route: GET /paciente/agendar/{nutricionista} Route Name: paciente.booking.schedule Parameters:
  • nutricionista (User): Route model binding
Returns: View with:
  • 4 weeks of available time slots
  • Nutritionist information
  • Schedule grid grouped by weeks and days
Validation:
  • Nutritionist must be clinically enabled
  • Patient cannot have pending appointments with ANY nutritionist
  • Nutritionist must have configured schedules
Redirects:
  • If nutritionist unavailable: redirect to booking index
  • If patient has pending appointment: redirect to dashboard with error
  • If nutritionist has no schedules: redirect to booking index
Example Response Structure:
$weeks = [
    [
        'week_number' => 1,
        'start_date' => Carbon instance,
        'start_date_formatted' => '3 Mar',
        'days' => [
            [
                'date' => Carbon instance,
                'date_formatted' => 'lunes, 3 de marzo',
                'day_name' => 'lunes',
                'day_number' => '03',
                'is_today' => false,
                'is_past' => false,
                'slots' => [
                    ['time' => '08:00', 'time_formatted' => '08:00 AM']
                ]
            ]
        ]
    ]
];

storeAppointment(Request request,Userrequest, User nutricionista)

Creates a new appointment booking.
public function storeAppointment(Request $request, User $nutricionista)
Route: POST /paciente/agendar/{nutricionista} Route Name: paciente.booking.store Parameters:
  • nutricionista (User): Route model binding
  • date (date, required): Appointment date (must be today or future)
  • time (string, required): Appointment time (HH:MM format)
  • reason (string, optional): Reason for appointment (max 500 chars)
  • appointment_type (string, required): Type (primera_vez, seguimiento, control)
Returns: Redirect to dashboard with success message Side Effects:
  • Creates appointment with “pendiente” state
  • Sets price from nutritionist settings (default $30.00)
  • Sends immediate notification to nutritionist
  • Sends delayed notification to patient (20 seconds)
Validation:
  • Nutritionist must be clinically enabled
  • Patient cannot have pending appointments
  • Time slot must be available
  • Schedule must exist for selected day and time
  • Time must be within nutritionist’s configured schedule range
Example:
[
    'date' => '2026-03-10',
    'time' => '14:00',
    'reason' => 'Quiero mejorar mi alimentación',
    'appointment_type' => 'primera_vez'
]

Appointment Management

appointments(Request $request)

Displays the patient’s appointment history with filtering.
public function appointments(Request $request)
Route: GET /paciente/citas Route Name: paciente.appointments.index Query Parameters:
  • estado (string, optional): Filter by appointment state
  • nutricionista (integer, optional): Filter by nutritionist ID
  • fecha_desde (date, optional): Start date filter
  • fecha_hasta (date, optional): End date filter
Returns: Paginated appointments (10 per page) with list of nutritionists the patient has seen Side Effects: Automatically marks expired appointments Example:
GET /paciente/citas?estado=completada&fecha_desde=2026-01-01

showAppointment(Appointment $appointment)

Displays detailed information about a specific appointment.
public function showAppointment(Appointment $appointment)
Route: GET /paciente/citas/{appointment} Route Name: paciente.appointments.show Parameters:
  • appointment (Appointment): Route model binding
Returns: View with complete appointment details including:
  • Nutritionist information and personal data
  • Appointment state
  • Attention data (if completed)
  • Patient personal data
Authorization: Verifies appointment belongs to authenticated patient (403 if not)

cancelAppointment(Appointment $appointment)

Cancels a patient’s appointment.
public function cancelAppointment(Appointment $appointment)
Route: POST /paciente/citas/{appointment}/cancelar Route Name: paciente.appointments.cancel Parameters:
  • appointment (Appointment): Route model binding
Returns: Redirect back with success message Side Effects:
  • Updates appointment state to “cancelada”
  • Sends immediate notification to patient
  • Sends delayed notification to nutritionist (20 seconds)
Authorization: Verifies appointment belongs to authenticated patient (403 if not)

Clinical History

history()

Displays the patient’s clinical history with progress charts.
public function history()
Route: GET /paciente/historial Route Name: paciente.history Returns: View with:
  • All attention records for the patient (all nutritionists)
  • Chart data for visualization
  • Progress statistics
  • Patient personal data
Chart Data Includes:
  • Weight progression over time
  • BMI changes
  • Body fat percentage
  • All body circumferences (waist, hip, neck, wrist, arms, thighs, calves)
  • Metabolic metrics (TMB, TDEE, target calories)
  • Health ratios (WHR, WHT)
Example Progress Data:
[
    'total_attentions' => 7,
    'first_date' => '15/01/2026',
    'last_date' => '05/03/2026',
    'weight' => [
        'initial' => 92.0,
        'current' => 85.0,
        'change' => -7.0,
        'percentage' => -7.6
    ],
    'bmi' => [
        'initial' => 28.5,
        'current' => 26.3,
        'change' => -2.2
    ],
    'body_fat' => [
        'initial' => 32.0,
        'current' => 28.5,
        'change' => -3.5
    ],
    // ... more metrics
]

Profile Management

profile()

Displays the patient profile page.
public function profile()
Route: GET /paciente/perfil Route Name: paciente.profile Returns: Patient profile view

updatePassword(Request $request)

Updates the patient’s password.
public function updatePassword(Request $request)
Route: POST /paciente/perfil/contrasena Route Name: paciente.profile.update-password Parameters:
  • current_password (string, required if not default): Current password
  • password (string, required): New password (min 8 chars)
  • password_confirmation (string, required): Password confirmation
Returns: Redirect to profile with success message Side Effects: Sends security notification email via PasswordChangedNotification Validation:
  • If user has non-default password, current password is required and must match
  • New password must be at least 8 characters
  • Password confirmation must match new password
Error Messages:
[
    'current_password.required' => 'Debes ingresar tu contraseña actual para cambiarla.',
    'password.required' => 'La nueva contraseña es obligatoria.',
    'password.min' => 'La nueva contraseña debe tener al menos 8 caracteres.',
    'password.confirmed' => 'Las contraseñas no coinciden.'
]
Example:
[
    'current_password' => 'OldPassword123',
    'password' => 'NewSecurePass456',
    'password_confirmation' => 'NewSecurePass456'
]

Private Helper Methods

prepareChartData($attentions)

Prepares attention data for Chart.js visualization.
private function prepareChartData($attentions)
Parameters:
  • $attentions (Collection): Collection of Attention models with attentionData
Returns: Array with chart-ready data:
[
    'labels' => ['01/01/2026', '15/01/2026', ...],
    'weights' => [92.0, 90.5, 88.0, ...],
    'bmis' => [28.5, 28.0, 27.3, ...],
    'bodyFats' => [32.0, 31.0, 29.5, ...],
    'waists' => [95.0, 93.0, 91.0, ...],
    'hips' => [105.0, 103.5, 102.0, ...],
    'necks' => [38.0, 37.5, 37.0, ...],
    'wrists' => [17.5, 17.5, 17.0, ...],
    'armContracted' => [35.0, 34.5, 34.0, ...],
    'armRelaxed' => [32.0, 31.5, 31.0, ...],
    'thighs' => [60.0, 59.0, 58.0, ...],
    'calves' => [38.0, 37.5, 37.0, ...],
    'tmbs' => [1850, 1820, 1790, ...],
    'tdees' => [2500, 2450, 2400, ...],
    'whrs' => [0.90, 0.89, 0.88, ...],
    'whts' => [0.55, 0.54, 0.53, ...],
    'targetCalories' => [2200, 2150, 2100, ...]
]

calculateProgressStats($attentions)

Calculates progress statistics comparing first and last attention.
private function calculateProgressStats($attentions)
Parameters:
  • $attentions (Collection): Collection of Attention models
Returns: Array with comprehensive progress metrics or null if insufficient data Calculated Metrics:
  • Total attentions count
  • First and last attention dates
  • Weight change (absolute and percentage)
  • BMI change
  • Body fat change
  • All circumference changes
  • Metabolic metrics changes (TMB, TDEE)

Business Rules

One Pending Appointment Rule

Patients can only have one pending appointment at a time (with any nutritionist). This prevents overbooking and ensures commitment. Enforced in:
  • selectSchedule() - Before showing available slots
  • storeAppointment() - Before creating appointment

Clinical Enablement

Patients can only book with nutritionists who are clinically enabled:
  • User account must be active (user_state = “activo”)
  • Nutritionist must have configured schedules
Checked via: $user->estaHabilitadoClinicamente() method

Time Slot Availability

Time slots are considered available when:
  • They are in the future (not past)
  • They fall within nutritionist’s configured schedule
  • No other pending appointment exists at that time
  • Duration is 45 minutes per slot

Dependencies

The PacienteController uses: Models:
  • App\Models\User
  • App\Models\Appointment
  • App\Models\AppointmentState
  • App\Models\NutricionistaSchedule
  • App\Models\Attention
Notifications:
  • App\Notifications\AppointmentCancelledByPatient
  • App\Notifications\AppointmentCreatedNotification
  • App\Notifications\AppointmentConfirmedForPatient
  • App\Notifications\PasswordChangedNotification
Other:
  • Carbon\Carbon for date/time manipulation
  • Illuminate\Support\Facades\Hash for password hashing

Build docs developers (and LLMs) love