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

341 lines
11 KiB
PHP
Raw Normal View History

<?php
namespace App\Models\Patient;
use App\Models\BaseModel;
2025-10-16 10:50:09 +07:00
use App\Models\Patient\PatAttModel;
use App\Models\Patient\PatComModel;
use App\Models\Patient\PatIdtModel;
2025-10-14 18:53:06 +07:00
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',
2025-10-21 10:27:31 +07:00
'DeathIndicator', 'TimeOfDeath', 'LinkTo', 'CreateDate', 'DelDate' ];
2025-10-13 15:09:55 +07:00
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
2025-10-14 10:48:20 +07:00
protected $useSoftDeletes = true;
protected $deletedField = 'DelDate';
public function getPatients($filters = []) {
$qname = "CONCAT_WS(' ', IFNULL(Prefix,''), IFNULL(NameFirst,''), IFNULL(NameMiddle,''), IFNULL(NameLast,''), IFNULL(NameMaiden,''), IFNULL(Suffix,''))";
$this->select("InternalPID, PatientID, $qname as FullName, vs.VDesc as Gender, Birthdate, EmailAddress1 as Email, MobilePhone");
$this->join('valueset vs', 'vs.vid = Gender', 'left');
if (!empty($filters['Name'])) {
2025-10-16 13:22:28 +07:00
$this->like($qname, $filters['Name'], 'both');
}
if (!empty($filters['InternalPID'])) {
2025-10-16 13:22:28 +07:00
$this->where('InternalPID', $filters['InternalPID']);
}
if (!empty($filters['PatientID'])) {
2025-10-16 13:22:28 +07:00
$this->like('PatientID', $filters['PatientID'], 'both');
}
if (!empty($filters['Birthdate'])) {
2025-10-16 13:22:28 +07:00
$this->where('Birthdate', $filters['Birthdate']);
}
2025-10-16 13:22:28 +07:00
return $this->findAll();
}
public function getPatient($InternalPID) {
2025-10-16 13:22:28 +07:00
$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,
2025-12-01 16:47:52 +07:00
areageo1.AreaGeoID as ProvinceID,
areageo1.AreaName as Province,
areageo2.AreaGeoID as CityID,
areageo2.AreaName as City
2025-10-23 10:20:55 +07:00
")
2025-10-16 13:22:28 +07:00
->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')
2025-12-01 16:47:52 +07:00
->join('areageo areageo1', 'areageo1.AreaGeoID = patient.Province', 'left')
->join('areageo areageo2', 'areageo2.AreaGeoID = patient.City', 'left')
2025-10-16 13:22:28 +07:00
->where('patient.InternalPID', (int) $InternalPID)
->findAll();
if (empty($rows)) { return null; }
$patient = $rows[0];
if (method_exists($this, 'transformPatientData')) { $patient = $this->transformPatientData($patient); }
2025-10-01 15:36:55 +07:00
unset($patient['Address']);
unset($patient['IdentifierType']);
unset($patient['Identifier']);
unset($patient['Comment']);
// Default nested structures
2025-10-01 15:36:55 +07:00
$patient['PatIdt'] = null;
$patient['PatAtt'] = [];
foreach ($rows as $row) {
2025-10-01 15:36:55 +07:00
if ($row['IdentifierType'] && $row['Identifier'] && !$patient['PatIdt']) {
$patient['PatIdt'] = [
'IdentifierType' => $row['IdentifierType'],
'Identifier' => $row['Identifier'],
];
}
if ($row['Address']) {
2025-10-01 15:36:55 +07:00
$patient['PatAtt'][] = ['Address' => $row['Address']];
}
}
2025-10-01 15:36:55 +07:00
if (empty($patient['PatIdt'])) { $patient['PatIdt'] = null; }
if (empty($patient['PatAtt'])) { $patient['PatAtt'] = null; }
return $patient;
}
public function createPatient($input) {
$db = \Config\Database::connect();
2025-10-16 10:50:09 +07:00
2025-10-16 13:22:28 +07:00
$modelPatAtt = new PatAttModel();
$modelPatCom = new PatComModel();
$modelPatIdt = new PatIdtModel();
$input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];
2025-10-16 10:50:09 +07:00
2025-10-14 15:50:22 +07:00
$db->transBegin();
try {
2025-10-16 10:50:09 +07:00
// Insert Data ke Tabel Patient, get ID dan cek apa ada error
$this->insert($input);
$newInternalPID = $this->getInsertID();
$this->checkDbError($db, 'Insert patient');
2025-10-16 10:50:09 +07:00
// Insert Data ke Tabel PatIdt
if (!empty($input['PatIdt'])) {
2025-10-16 13:22:28 +07:00
$modelPatIdt->createPatIdt($input['PatIdt'], $newInternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Insert PatIdt');
}
2025-10-16 10:50:09 +07:00
// Insert Data ke Tabel PatCom
if (!empty($input['PatCom'])) {
2025-10-16 13:22:28 +07:00
$modelPatCom->createPatCom($input['PatCom'], $newInternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Insert PatCom');
2025-10-01 15:36:55 +07:00
}
2025-10-16 10:50:09 +07:00
// Insert Data ke Tabel PatAtt
if (!empty($input['PatAtt'])) {
2025-10-16 13:22:28 +07:00
$modelPatAtt->createPatAtt($input['PatAtt'], $newInternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Insert PatAtt');
}
2025-10-14 15:50:22 +07:00
$db->transCommit();
2025-10-16 10:50:09 +07:00
return $newInternalPID;
2025-10-01 15:36:55 +07:00
} catch (\Exception $e) {
2025-10-14 15:50:22 +07:00
$db->transRollback();
2025-10-01 15:36:55 +07:00
throw $e;
}
}
public function updatePatient($input) {
$db = \Config\Database::connect();
2025-10-16 13:22:28 +07:00
$modelPatIdt = new PatIdtModel();
$modelPatCom = new PatComModel();
$modelPatAtt = new PatAttModel();
2025-10-16 10:50:09 +07:00
$input['LinkTo'] = empty($input['LinkTo']) ? null : $input['LinkTo'];
2025-10-14 15:50:22 +07:00
$db->transBegin();
2025-10-01 15:36:55 +07:00
try {
2025-10-16 10:50:09 +07:00
// Update Patient
2025-10-01 15:36:55 +07:00
$InternalPID = $input['InternalPID'];
$this->where('InternalPID',$InternalPID)->set($input)->update();
$this->checkDbError($db, 'Update patient');
2025-10-16 10:50:09 +07:00
// Update Patidt
if (!empty($input['PatIdt'])) {
2025-10-16 13:22:28 +07:00
$modelPatIdt->updatePatIdt($input['PatIdt'], $InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update patIdt');
2025-10-01 15:36:55 +07:00
} else {
2025-10-16 13:22:28 +07:00
$modelPatIdt->deletePatIdt($InternalPID);
$this->checkDbError($db, 'Update patidt');
2025-10-01 15:36:55 +07:00
}
2025-10-16 10:50:09 +07:00
// Update Patcom
if (!empty($input['PatCom'])) {
2025-10-16 13:22:28 +07:00
$modelPatCom->updatePatCom($input['PatCom'], $InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update PatCom');
2025-10-01 15:36:55 +07:00
} else {
2025-10-16 13:22:28 +07:00
$modelPatCom->deletePatCom($InternalPID);
$this->checkDbError($db, 'Update patcom');
2025-10-01 15:36:55 +07:00
}
2025-10-16 10:50:09 +07:00
// Update Patatt
if (!empty($input['PatAtt'])) {
2025-10-16 13:22:28 +07:00
$modelPatAtt->updatePatAtt($input['PatAtt'], $InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update PatAtt');
2025-10-01 15:36:55 +07:00
} else {
2025-10-16 13:22:28 +07:00
$modelPatAtt->deletePatAtt($InternalPID);
2025-10-16 10:50:09 +07:00
$this->checkDbError($db, 'Update/Delete patatt');
2025-10-01 15:36:55 +07:00
}
2025-10-14 15:50:22 +07:00
$db->transCommit();
2025-10-16 10:50:09 +07:00
2025-10-01 15:36:55 +07:00
return $InternalPID;
2025-10-14 15:50:22 +07:00
} catch (\Exception $e) {
2025-10-14 15:50:22 +07:00
$db->transRollback();
2025-10-01 15:36:55 +07:00
throw $e;
}
}
private function transformPatientData(array $patient): array {
2025-10-21 10:27:31 +07:00
$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']);
2025-10-21 09:33:38 +07:00
$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;
}
2025-10-16 13:22:28 +07:00
return $this->select('InternalPID, PatientID')
->where('InternalPID', (int) $custodianId)
->first() ?: null;
}
2025-10-14 15:50:22 +07:00
// 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 {
2025-10-14 15:50:22 +07:00
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;
}
}
2025-10-14 15:50:22 +07:00
// 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;
}
}
}