Skip to main content

Overview

The patient management system provides a complete profile for each patient, including personal demographics, contact information, medical history, and address details. Patients are scoped to specific clinics in the multi-tenant architecture.

Complete Profiles

Demographics, contact info, and medical data in one place

Clinic Scoped

Each patient belongs to a specific clinic

Mobile Access

Patients can access their records via mobile app

Clinical Records

Linked to comprehensive clinical files and evolution notes

Data Model

The Paciente model stores comprehensive patient information with strong relationships to clinics and clinical records.

Database Schema

See migration at ~/workspace/source/database/migrations/2026_02_23_090302_create_paciente_table.php:14-49
Schema::create('paciente', function (Blueprint $table) {
    $table->id('id_paciente');
    
    $table->unsignedBigInteger('id_clinica');
    
    // Personal information
    $table->string('nombre');
    $table->string('apellido_paterno');
    $table->string('apellido_materno')->nullable();
    
    $table->date('fecha_nacimiento')->nullable();
    $table->enum('sexo', ['hombre', 'mujer'])->nullable();
    
    $table->string('telefono')->nullable();
    $table->string('curp')->nullable()->unique();
    $table->string('ocupacion')->nullable();
    $table->decimal('peso', 5, 2)->nullable();
    
    // Address
    $table->string('calle')->nullable();
    $table->string('num_ext')->nullable();
    $table->string('num_int')->nullable();
    $table->string('colonia')->nullable();
    $table->string('ciudad')->nullable();
    $table->string('estado')->nullable();
    $table->string('codigo_postal')->nullable();
    
    $table->enum('estatus', ['activo', 'baja'])->default('activo');
    
    $table->timestamps();
    
    // Foreign key to clinic
    $table->foreign('id_clinica')
          ->references('id_clinica')
          ->on('clinica')
          ->onDelete('cascade');
});

Model Attributes

See complete model at ~/workspace/source/app/Models/Paciente.php:16-35
FieldTypeDescription
id_pacientebigintPrimary key
id_clinicabigintForeign key to clinic (required)
nombrestringFirst name
apellido_paternostringPaternal surname (required)
apellido_maternostringMaternal surname (optional)
fecha_nacimientodateDate of birth
sexoenumGender: hombre or mujer
telefonostringContact phone number
curpstringMexican national ID (unique, 18 chars)
ocupacionstringOccupation/profession
pesodecimal(5,2)Weight in kilograms
callestringStreet address
num_extstringExternal number
num_intstringInternal number/apartment
coloniastringNeighborhood
ciudadstringCity
estadostringState/province
codigo_postalstringPostal code
estatusenumStatus: activo or baja
CURP (Clave Única de Registro de Población) is Mexico’s unique population registry code. It’s similar to a social security number and consists of 18 alphanumeric characters.

Relationships

The Paciente model establishes critical relationships with multiple entities in the system.

Belongs To Clinic

// Patient belongs to one clinic
public function clinica()
{
    return $this->belongsTo(Clinica::class, 'id_clinica', 'id_clinica');
}
When a clinic is deleted, all its patients are automatically removed due to the onDelete('cascade') constraint.

Has Many Appointments

See ~/workspace/source/app/Models/Paciente.php:45-48
// Patient can have multiple appointments
public function citas()
{
    return $this->hasMany(Cita::class, 'id_paciente', 'id_paciente');
}

Has One Clinical File

See ~/workspace/source/app/Models/Paciente.php:51-54
// Patient has one clinical record
public function expediente()
{
    return $this->hasOne(ExpedienteClinico::class, 'id_paciente', 'id_paciente');
}
The clinical file (ExpedienteClinico) stores:
  • Hereditary medical history
  • Pathological history
  • Allergies
  • General observations

Has One Mobile Access

See ~/workspace/source/app/Models/Paciente.php:57-60
// Patient can have mobile app access
public function accesoMovil()
{
    return $this->hasOne(AccesoMovil::class, 'id_paciente', 'id_paciente');
}

Entity Relationship Diagram

Patient Demographics

Personal Information

The system captures essential demographic data:

Full Name

Three-part name: first name + paternal surname + maternal surname (Mexican standard)

Date of Birth

Used to calculate age and track pediatric vs adult patients

Gender

Binary options: hombre (male) or mujer (female)

CURP

Mexican national ID for official identification

Contact Information

  • Stored as string to preserve formatting
  • Typically 10 digits for Mexican numbers
  • Used for appointment reminders and contact
Full address breakdown:
  • Street name (calle)
  • External number (num_ext)
  • Internal number/apt (num_int)
  • Neighborhood (colonia)
  • City (ciudad)
  • State (estado)
  • Postal code (codigo_postal)

Medical Information

Weight

Stored in kilograms with 2 decimal precision (e.g., 75.50 kg)

Occupation

Patient’s profession, may be relevant for certain dental conditions

Mobile Access Feature

Patients can be granted access to a mobile application to view their records, appointments, and treatment history.

AccesoMovil Schema

See migration at ~/workspace/source/database/migrations/2026_02_23_093639_create_acceso_movil_table.php:14-35
Schema::create('acceso_movil', function (Blueprint $table) {
    $table->id('id_acceso');
    
    $table->unsignedBigInteger('id_paciente')->unique();
    
    $table->string('usuario_movil')->unique();
    $table->string('password');
    
    $table->string('token')->nullable()->unique();
    $table->dateTime('fecha_expiracion')->nullable();
    
    $table->enum('estatus', ['activo', 'expirado', 'temporal'])
          ->default('temporal');
    
    $table->timestamps();
    
    $table->foreign('id_paciente')
          ->references('id_paciente')
          ->on('paciente')
          ->onDelete('cascade');
});

Mobile Access Workflow

1

Create Credentials

Admin or staff creates unique username and temporary password for patient
2

Initial Login

Patient logs in with temporary credentials (status = temporal)
3

Password Change

System prompts patient to change password on first login
4

Token Generation

After successful login, system generates authentication token with expiration
5

Active Access

Status changes to activo, patient can access mobile features

Mobile Access States

StatusDescription
temporalInitial state, requires password change
activoFull access granted
expiradoToken expired, requires re-authentication
Ensure passwords are hashed before storage using Laravel’s Hash facade or bcrypt(). Never store plain-text passwords.

Patient Status Management

Patients can be marked as active or inactive without deleting their historical records.

Status Values

Activo

Patient is current and can receive appointments/treatments

Baja

Patient is inactive (moved, deceased, or no longer visiting). Records preserved.
Setting a patient to baja status should prevent scheduling new appointments while maintaining all historical data for legal and medical records requirements.

Creating a Patient

While the controller code isn’t provided, patient creation would typically follow this pattern:
// Example patient creation
$paciente = Paciente::create([
    'id_clinica' => auth()->user()->id_clinica, // Current user's clinic
    'nombre' => 'Juan',
    'apellido_paterno' => 'García',
    'apellido_materno' => 'Rodríguez',
    'fecha_nacimiento' => '1990-05-15',
    'sexo' => 'hombre',
    'telefono' => '5512345678',
    'curp' => 'GARJ900515HDFXXX01',
    'peso' => 75.5,
    'calle' => 'Av. Insurgentes',
    'num_ext' => '123',
    'colonia' => 'Roma Norte',
    'ciudad' => 'Ciudad de México',
    'estado' => 'CDMX',
    'codigo_postal' => '06700',
    'estatus' => 'activo'
]);

Searching Patients

Common search patterns for patient lookup:

By Name

$pacientes = Paciente::where('id_clinica', $clinicaId)
    ->where(function($query) use ($searchTerm) {
        $query->where('nombre', 'LIKE', "%{$searchTerm}%")
              ->orWhere('apellido_paterno', 'LIKE', "%{$searchTerm}%")
              ->orWhere('apellido_materno', 'LIKE', "%{$searchTerm}%");
    })
    ->where('estatus', 'activo')
    ->get();

By Phone Number

$paciente = Paciente::where('id_clinica', $clinicaId)
    ->where('telefono', $phoneNumber)
    ->first();

By CURP

$paciente = Paciente::where('curp', $curp)
    ->first();
CURP is unique across the entire system (not just per clinic), so it can be used for global patient lookup.

Data Privacy and Security

CURP Encryption

The Paciente model includes a commented-out encryption option for sensitive data:
protected $casts = [
    // 'curp' => 'encrypted', // Can be enabled for enhanced privacy
];
To encrypt CURP values at the database level:
  1. Uncomment the cast in Paciente.php
  2. Ensure APP_KEY is set in .env
  3. Laravel will automatically encrypt/decrypt on save/retrieve
Note: Encrypted fields cannot be searched directly with SQL queries.

Multi-Tenant Data Isolation

All patient queries should be scoped by clinic:
// GOOD: Scoped to clinic
$pacientes = Paciente::where('id_clinica', auth()->user()->id_clinica)->get();

// BAD: Could expose other clinics' data
$pacientes = Paciente::all();
Always filter by id_clinica to prevent cross-tenant data leakage. Consider using Laravel’s global scopes for automatic filtering.

Patient Lifecycle

Best Practices

Mexican naming conventions use three parts: first name + paternal surname + maternal surname. All three should be captured when available.
CURP has a specific 18-character format. Implement validation to ensure data quality:
  • Pattern: ^[A-Z]{4}[0-9]{6}[HM][A-Z]{5}[0-9]{2}$
  • First 4 letters: surname + name initials
  • 6 digits: birthdate (YYMMDD)
  • H/M: gender
  • 5 letters: birthplace + consonants
  • 2 digits: verification
Always scope patient queries by id_clinica to maintain multi-tenant isolation.
Use estatus = 'baja' instead of deleting patient records. Medical records must be retained for legal compliance.
  • Hash passwords using bcrypt
  • Implement token expiration
  • Force password change on first login
  • Log access attempts

Clinical Records

View patient medical history and evolution notes

Appointments

Schedule patient appointments

Treatments

Manage patient treatments

Build docs developers (and LLMs) love