db->table($this->table); $builder->select("InternalPID, PatientID, $qname as FullName, Gender, Birthdate, EmailAddress1 as Email, MobilePhone"); if (!empty($filters['Name'])) { $rawSql = new RawSql($qname); $builder->like($rawSql, $filters['Name'], 'both'); } if (!empty($filters['InternalPID'])) { $builder->where('InternalPID', $filters['InternalPID']); } if (!empty($filters['PatientID'])) { $builder->like('PatientID', $filters['PatientID'], 'both'); } if (!empty($filters['Birthdate'])) { $builder->where('Birthdate', $filters['Birthdate']); } return $builder->get()->getResultArray(); } public function getPatient($InternalPID) { $rows = $this->db->table('patient p') ->select(" p.*, country.VDesc as Country, race.VDesc as Race, religion.VDesc as Religion, ethnic.VDesc as Ethnic, patcom.Comment as Comment, patidt.IdentifierType, patidt.Identifier, patatt.Address ") ->join('valueset country', 'country.VID = p.Country', 'left') ->join('valueset race', 'race.VID = p.Race', 'left') ->join('valueset religion', 'religion.VID = p.Religion', 'left') ->join('valueset ethnic', 'ethnic.VID = p.Ethnic', 'left') ->join('patcom', 'patcom.InternalPID = p.InternalPID', 'left') ->join('patidt', 'patidt.InternalPID = p.InternalPID', 'left') ->join('patatt', 'patatt.InternalPID = p.InternalPID and patatt.DelDate is null', 'left') ->where('p.InternalPID', (int) $InternalPID) ->get() ->getResultArray(); if (empty($rows)) { return null; } $patient = $rows[0]; unset($patient['Address']); if (method_exists($this, 'transformPatientData')) { $patient = $this->transformPatientData($patient); } // Default nested structures $patient['PatIdt'] = null; $patient['PatCom'] = null; $patient['PatAtt'] = []; foreach ($rows as $row) { if ($row['IdentifierType'] && $row['Identifier'] && !$patient['PatIdt']) { $patient['PatIdt'] = [ 'IdentifierType' => $row['IdentifierType'], 'Identifier' => $row['Identifier'], ]; } if ($row['Comment']) { $patient['PatCom'] = $row['Comment']; } 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(); $patidt = $input['PatIdt'] ?? []; $patatt = $input['PatAtt'] ?? []; $patcom['Comment'] = $input['Comment'] ?? []; try { $db->transStart(); $this->insert($input); $newInternalPID = $this->getInsertID(); if (!empty($patidt)) { $patidt['InternalPID'] = $newInternalPID; $db->table('patidt')->insert($patidt); } if (!empty($patcom['Comment'])) { $patcom['InternalPID'] = $newInternalPID; $db->table('patcom')->insert($patcom); } if (!empty($patatt)) { foreach ($patatt as &$row) { $row['InternalPID'] = $newInternalPID; } $db->table('patatt')->upsertBatch($patatt); } $db->transComplete(); if ($db->transStatus() === false) { $error = $db->error(); throw new \Exception('Transaction failed: ' . ($error['message'] ?? 'Unknown DB error')); } return $newInternalPID; } catch (\Exception $e) { $db->transRollback(); throw $e; } } public function updatePatient($input) { $db = \Config\Database::connect(); $patidt = $input['PatIdt'] ?? []; $patatt = $input['PatAtt'] ?? []; $patcom['Comment'] = $input['PatCom'] ?? []; try { $db->transStart(); $InternalPID = $input['InternalPID']; $this->where('InternalPID',$InternalPID)->set($input)->update(); if (!empty($patidt)) { $exists = $db->table('patidt')->where('InternalPID', $InternalPID)->get()->getRowArray(); if ($exists) { $db->table('patidt')->where('InternalPID', $InternalPID)->update($patidt); } else { $patidt['InternalPID'] = $InternalPID; $db->table('patidt')->insert($patidt); } } else { $db->table('patidt')->where('InternalPID', $InternalPID)->delete(); } if (!empty($patcom)) { $exists = $db->table('patcom')->where('InternalPID', $InternalPID)->get()->getRowArray(); if ($exists) { $db->table('patcom')->where('InternalPID', $InternalPID)->update($patcom); } else { $patcom['InternalPID'] = $InternalPID; $db->table('patcom')->insert($patcom); } } else { $db->table('patcom')->where('InternalPID', $InternalPID)->delete(); } $now = date('Y-m-d H:i:s'); if (!empty($patatt)) { // Ambil daftar address aktif (DelDate IS NULL) di DB $oldActive = $db->table('patatt') ->select('Address') ->where('InternalPID', $InternalPID) ->where('DelDate', null) ->get()->getResultArray(); $oldActive = array_column($oldActive, 'Address'); // Normalisasi & dedup input baru (berdasarkan Address) $mapNew = []; foreach ($patatt as $row) { if (!isset($row['Address'])) continue; $mapNew[$row['Address']] = $row; // overwrite duplikat di input } $newData = array_keys($mapNew); // Hitung yang perlu ditambah & dihapus $added = array_diff($newData, $oldActive); // baru (belum aktif) $removed = array_diff($oldActive, $newData); // dulu aktif tapi hilang di input // 1) Soft delete yang dihapus if (!empty($removed)) { $db->table('patatt') ->where('InternalPID', $InternalPID) ->whereIn('Address', $removed) ->set('DelDate', $now) ->update(); } // 2) Tambahkan yang baru foreach ($added as $addr) { $data = $mapNew[$addr]; $data['InternalPID'] = $InternalPID; // Coba REACTIVATE satu baris yang pernah di-soft delete (kalau ada) $builder = $db->table('patatt'); $builder->set('DelDate', null); // Kalau ada kolom lain yang mau di-update saat re-activate, set di sini juga // mis: $builder->set('Note', $data['Note'] ?? null); $builder->where('InternalPID', $InternalPID) ->where('Address', $addr) ->where('(DelDate IS NOT NULL)', null, false) ->orderBy('PatAttID', 'DESC') ->limit(1) ->update(); if ($db->affectedRows() === 0) { // Tidak ada baris soft-deleted untuk alamat ini → INSERT baru $db->table('patatt')->insert($data); } } } else { // Input kosong → semua yang masih aktif di-soft delete $db->table('patatt')->where('InternalPID', $InternalPID)->where('DelDate', null)->set('DelDate', $now)->update(); } $db->transComplete(); if ($db->transStatus() === false) { throw new \Exception('Failed to sync patient relations'); } return $InternalPID; } catch (\Exception $e) { $db->transRollback(); throw $e; } } private function transformPatientData(array $patient): array { $patient["Age"] = $this->calculateAgeFromBirthdate($patient["Birthdate"]); $patient["Birthdate"] = $this->formatedDate($patient["Birthdate"]); $patient["CreateDate"] = $this->formatedDate($patient["CreateDate"]); $patient["DelDate"] = $this->formatedDate($patient["DelDate"]); $patient["DeathDateTime"] = $this->formatedDate($patient["DeathDateTime"]); $patient["BirthdateConversion"] = $this->formatedDateForDisplay($patient["Birthdate"]); $patient["LinkTo"] = $this->getLinkedPatients($patient['LinkTo']); $patient["Custodian"] = $this->getCustodian($patient['Custodian']); 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->db->table('patient') ->select('InternalPID, PatientID') ->where('InternalPID', (int) $custodianId) ->get() ->getRowArray() ?: null; } // Conversion to (Years Months Days) private function calculateAgeFromBirthdate($birthdate) { $dob = new \DateTime($birthdate); $today = new \DateTime(); $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 H:i private function formatedDate($dateString) { $date = \DateTime::createFromFormat('Y-m-d H:i', $dateString); if (!$date) { $timestamp = strtotime($dateString); if ($timestamp) { return date('Y-m-d H:i', $timestamp); } return null; } return $date->format('Y-m-d H:i'); } // Conversion Time to Format j M Y 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'); } }