clqms-be/app/Models/Patient/PatientModel.php

337 lines
11 KiB
PHP

<?php
namespace App\Models\Patient;
use App\Models\BaseModel;
use App\Models\Patient\PatAttModel;
use App\Models\Patient\PatComModel;
use App\Models\Patient\PatIdtModel;
class PatientModel extends BaseModel {
protected $table = 'patient';
protected $primaryKey = 'InternalPID';
protected $allowedFields = ['PatientID', 'AlternatePID', 'Prefix', 'NameFirst', 'NameMiddle', 'NameMaiden', 'NameLast', 'Suffix', 'NameAlias', 'Gender', 'Birthdate', 'PlaceOfBirth', 'Street_1', 'Street_2', 'Street_3',
'City', 'Province', 'ZIP', 'EmailAddress1', 'EmailAddress2', 'Phone', 'MobilePhone', 'Custodian', 'AccountNumber', 'Country', 'Race', 'MaritalStatus', 'Religion', 'Ethnic', 'Citizenship',
'DeathIndicator', 'TimeOfDeath', 'LinkTo', 'CreateDate', 'DelDate' ];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
protected $useSoftDeletes = true;
protected $deletedField = 'DelDate';
public function getPatients($filters = []) {
$qname = "LOWER(CONCAT_WS(' ', IFNULL(Prefix,''), IFNULL(NameFirst,''), IFNULL(NameMiddle,''), IFNULL(NameLast,''), IFNULL(NameMaiden,''), IFNULL(Suffix,'')))";
$this->select("InternalPID, PatientID, $qname as FullName, Gender, Birthdate, EmailAddress1 as Email, MobilePhone");
if (!empty($filters['Name'])) {
$this->like($qname, $filters['Name'], 'both');
}
if (!empty($filters['InternalPID'])) {
$this->where('InternalPID', $filters['InternalPID']);
}
if (!empty($filters['PatientID'])) {
$this->like('PatientID', $filters['PatientID'], 'both');
}
if (!empty($filters['Birthdate'])) {
$this->where('Birthdate', $filters['Birthdate']);
}
return $this->findAll();
}
public function getPatient($InternalPID) {
$rows = $this->select("
patient.*,
country.VDesc as Country,
country.VID as CountryVID,
race.VDesc as Race,
race.VID as RaceVID,
religion.VDesc as Religion,
religion.VID as ReligionVID,
ethnic.VDesc as Ethnic,
ethnic.VID as EthnicVID,
gender.VDesc as Gender,
gender.VID as GenderVID,
deathindicator.VDesc as DeathIndicator,
deathindicator.VID as DeathIndicatorVID,
maritalstatus.VDesc as MaritalStatus,
maritalstatus.VID as MaritalStatusVID,
patcom.Comment as Comment,
patidt.IdentifierType,
patidt.Identifier,
patatt.Address,
zones1.zonename as Province,
zones2.zonename as City
")
->join('valueset country', 'country.VID = patient.Country', 'left')
->join('valueset race', 'race.VID = patient.Race', 'left')
->join('valueset religion', 'religion.VID = patient.Religion', 'left')
->join('valueset ethnic', 'ethnic.VID = patient.Ethnic', 'left')
->join('valueset gender', 'gender.VID = patient.Gender', 'left')
->join('valueset deathindicator', 'deathindicator.VID = patient.DeathIndicator', 'left')
->join('valueset maritalstatus', 'maritalstatus.VID = patient.MaritalStatus', 'left')
->join('patcom', 'patcom.InternalPID = patient.InternalPID', 'left')
->join('patidt', 'patidt.InternalPID = patient.InternalPID', 'left')
->join('patatt', 'patatt.InternalPID = patient.InternalPID and patatt.DelDate is null', 'left')
->join('zones zones1', 'zones1.zoneid = patient.Province', 'left')
->join('zones zones2', 'zones2.zoneid = patient.City', 'left')
->where('patient.InternalPID', (int) $InternalPID)
->findAll();
if (empty($rows)) { return null; }
$patient = $rows[0];
if (method_exists($this, 'transformPatientData')) { $patient = $this->transformPatientData($patient); }
unset($patient['Address']);
unset($patient['IdentifierType']);
unset($patient['Identifier']);
unset($patient['Comment']);
// Default nested structures
$patient['PatIdt'] = null;
$patient['PatAtt'] = [];
foreach ($rows as $row) {
if ($row['IdentifierType'] && $row['Identifier'] && !$patient['PatIdt']) {
$patient['PatIdt'] = [
'IdentifierType' => $row['IdentifierType'],
'Identifier' => $row['Identifier'],
];
}
if ($row['Address']) {
$patient['PatAtt'][] = ['Address' => $row['Address']];
}
}
if (empty($patient['PatIdt'])) { $patient['PatIdt'] = null; }
if (empty($patient['PatAtt'])) { $patient['PatAtt'] = null; }
return $patient;
}
public function createPatient($input) {
$db = \Config\Database::connect();
$modelPatAtt = new PatAttModel();
$modelPatCom = new PatComModel();
$modelPatIdt = new PatIdtModel();
$input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];
$db->transBegin();
try {
// Insert Data ke Tabel Patient, get ID dan cek apa ada error
$this->insert($input);
$newInternalPID = $this->getInsertID();
$this->checkDbError($db, 'Insert patient');
// Insert Data ke Tabel PatIdt
if (!empty($input['PatIdt'])) {
$modelPatIdt->createPatIdt($input['PatIdt'], $newInternalPID);
$this->checkDbError($db, 'Insert PatIdt');
}
// Insert Data ke Tabel PatCom
if (!empty($input['PatCom'])) {
$modelPatCom->createPatCom($input['PatCom'], $newInternalPID);
$this->checkDbError($db, 'Insert PatCom');
}
// Insert Data ke Tabel PatAtt
if (!empty($input['PatAtt'])) {
$modelPatAtt->createPatAtt($input['PatAtt'], $newInternalPID);
$this->checkDbError($db, 'Insert PatAtt');
}
$db->transCommit();
return $newInternalPID;
} catch (\Exception $e) {
$db->transRollback();
throw $e;
}
}
public function updatePatient($input) {
$db = \Config\Database::connect();
$modelPatIdt = new PatIdtModel();
$modelPatCom = new PatComModel();
$modelPatAtt = new PatAttModel();
$input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];
$db->transBegin();
try {
// Update Patient
$InternalPID = $input['InternalPID'];
$this->where('InternalPID',$InternalPID)->set($input)->update();
$this->checkDbError($db, 'Update patient');
// Update Patidt
if (!empty($input['PatIdt'])) {
$modelPatIdt->updatePatIdt($input['PatIdt'], $InternalPID);
$this->checkDbError($db, 'Update patIdt');
} else {
$modelPatIdt->deletePatIdt($InternalPID);
$this->checkDbError($db, 'Update patidt');
}
// Update Patcom
if (!empty($input['PatCom'])) {
$modelPatCom->updatePatCom($input['PatCom'], $InternalPID);
$this->checkDbError($db, 'Update PatCom');
} else {
$modelPatCom->deletePatCom($InternalPID);
$this->checkDbError($db, 'Update patcom');
}
// Update Patatt
if (!empty($input['PatAtt'])) {
$modelPatAtt->updatePatAtt($input['PatAtt'], $InternalPID);
$this->checkDbError($db, 'Update PatAtt');
} else {
$modelPatAtt->deletePatAtt($InternalPID);
$this->checkDbError($db, 'Update/Delete patatt');
}
$db->transCommit();
return $InternalPID;
} catch (\Exception $e) {
$db->transRollback();
throw $e;
}
}
private function transformPatientData(array $patient): array {
$patient["Age"] = $this->calculateAgeFromBirthdate($patient["Birthdate"], $patient["TimeOfDeath"]);
$patient["TimeOfDeath"] = $this->formattedDate($patient["TimeOfDeath"]);
$patient["CreateDate"] = $this->formattedDate($patient["CreateDate"]);
$patient["BirthdateConversion"] = $this->formatedDateForDisplay($patient["Birthdate"]);
$patient["LinkTo"] = $this->getLinkedPatients($patient['LinkTo']);
$patient["Custodian"] = $this->getCustodian($patient['Custodian']);
$patient['PatCom'] = $patient['Comment'];
return $patient;
}
private function getLinkedPatients(?string $linkTo): ?array {
if (empty($linkTo)) { return null; }
$ids = array_filter(explode(',', $linkTo));
return $this->db->table('patient')
->select('InternalPID, PatientID')
->whereIn('InternalPID', $ids)
->get()
->getResultArray() ?: null;
}
private function getCustodian($custodianId): ?array {
if (empty($custodianId)) {
return null;
}
return $this->select('InternalPID, PatientID')
->where('InternalPID', (int) $custodianId)
->first() ?: null;
}
// Conversion to (Years Months Days) - For Age
private function calculateAgeFromBirthdate($birthdate, $deathdate) {
$dob = new \DateTime($birthdate);
// Cek DeathTime
if ($deathdate == null) {
$today = new \DateTime();
} else {
$deathdate = new \DateTime($deathdate);
$today = $deathdate;
}
$diff = $today->diff($dob);
$formattedAge = "";
if ($diff->y > 0){
$formattedAge .= "{$diff->y} Years ";
}
if ($diff->m > 0){
$formattedAge .= "{$diff->m} Months ";
}
if ($diff->d > 0){
$formattedAge .= "{$diff->d} Days";
}
return $formattedAge;
}
// Conversion Time to Format Y-m-d\TH:i:s\Z
private function formattedDate(?string $dateString): ?string {
try {
if (empty($dateString)) {return null;}
$dt = new \DateTime($dateString, new \DateTimeZone("UTC"));
return $dt->format('Y-m-d\TH:i:s\Z'); // ISO 8601 UTC
} catch (\Exception $e) {
return null;
}
}
// Conversion Time to Format j M Y - For BirthdateConversion
private function formatedDateForDisplay($dateString) {
$date = \DateTime::createFromFormat('Y-m-d H:i', $dateString);
if (!$date) {
$timestamp = strtotime($dateString);
if ($timestamp) {
return date('j M Y', $timestamp);
}
return null;
}
return $date->format('j M Y');
}
// Check Error and Send Spesific Messages
private function checkDbError($db, string $context) {
$error = $db->error();
if (!empty($error['code'])) {
throw new \Exception(
"{$context} failed: {$error['code']} - {$error['message']}"
);
}
}
// Preventif 0000-00-00
private function isValidDateTime($datetime) {
if (empty($datetime) || $datetime=="") {return null; }
try {
// Kalau input hanya Y-m-d (tanpa jam)
if (preg_match('/^\d{4}-\d{2}-\d{2}$/', $datetime)) {
$dt = \DateTime::createFromFormat('Y-m-d', $datetime);
return $dt ? $dt->format('Y-m-d') : null; // hanya tanggal
}
// Selain itu (ISO 8601 atau datetime lain), format ke Y-m-d H:i:s
$dt = new \DateTime($datetime);
return $dt->format('Y-m-d H:i:s');
} catch (\Exception $e) {
return null;
}
}
}