File: /mnt/data/ghayatcom/ghayatcom-api/app/Services/FhirService.php
<?php
namespace App\Services;
use GuzzleHttp\Client;
use GuzzleHttp\Exception\RequestException;
use Illuminate\Support\Facades\Log;
use Faker\Factory as FakerFactory;
use Carbon\Carbon;
class FhirService
{
protected $client;
protected $config;
protected $faker;
public function __construct()
{
$this->faker = FakerFactory::create();
$this->config = config('services.fhir');
$this->client = new Client([
'base_uri' => $this->config['base_url'],
'timeout' => $this->config['timeout'],
'headers' => $this->config['default_headers'],
'verify' => false
]);
}
protected function getAuthHeaders()
{
switch ($this->config['auth_type']) {
case 'basic':
return [
'auth' => [
$this->config['auth_credentials']['username'],
$this->config['auth_credentials']['password'],
],
];
case 'oauth2':
return [
'headers' => [
'Authorization' => 'Bearer ' . $this->getOAuthToken(),
],
];
case 'token':
return [
'headers' => [
'Authorization' => 'Bearer ' . $this->config['auth_credentials']['token'],
],
];
default:
return [];
}
}
protected function getOAuthToken()
{
// Implement OAuth2 token retrieval logic
// This is a simplified version - you may need to adjust based on your FHIR server's OAuth2 implementation
$tokenClient = new Client([
'base_uri' => $this->config['base_url'],
]);
$response = $tokenClient->post('/oauth2/token', [
'form_params' => [
'grant_type' => 'client_credentials',
'client_id' => $this->config['auth_credentials']['client_id'],
'client_secret' => $this->config['auth_credentials']['client_secret'],
'scope' => 'user/*.*',
],
]);
$data = json_decode($response->getBody(), true);
return $data['access_token'];
}
public function getResource($resourceType, $id, array $params = [])
{
try {
$url = "/{$resourceType}/{$id}";
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
$response = $this->client->get($url, $this->getAuthHeaders());
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
Log::error("FHIR GET failed: " . $e->getMessage());
return null;
}
}
public function searchResource($resourceType, array $params = [])
{
try {
$url = "/{$resourceType}";
if (!empty($params)) {
$url .= '?' . http_build_query($params);
}
$response = $this->client->get($url, $this->getAuthHeaders());
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
Log::error("FHIR SEARCH failed: " . $e->getMessage());
return null;
}
}
public function createResource($resourceType, array $data)
{
try {
$response = $this->client->post("/{$resourceType}", array_merge(
$this->getAuthHeaders(),
['json' => $data]
));
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
Log::error("FHIR CREATE failed: " . $e->getMessage());
return null;
}
}
public function updateResource($resourceType, $id, array $data)
{
try {
$response = $this->client->put("/{$resourceType}/{$id}", array_merge(
$this->getAuthHeaders(),
['json' => $data]
));
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
Log::error("FHIR UPDATE failed: " . $e->getMessage());
return null;
}
}
public function deleteResource($resourceType, $id)
{
try {
$response = $this->client->delete("/{$resourceType}/{$id}", $this->getAuthHeaders());
return $response->getStatusCode() === 204;
} catch (RequestException $e) {
Log::error("FHIR DELETE failed: " . $e->getMessage());
return false;
}
}
public function getPatientEverything($patientId)
{
try {
$response = $this->client->get("/Patient/{$patientId}/\$everything", $this->getAuthHeaders());
return json_decode($response->getBody(), true);
} catch (RequestException $e) {
Log::error("FHIR Patient $everything failed: " . $e->getMessage());
return null;
}
}
public function generatePatient($patient)
{
$gender = !empty($patient->gender) ? $patient->gender : '';
return [
'resourceType' => 'Patient',
'identifier' => [
[
'system' => config('custom.login_link'),
'value' => $patient->id
]
],
'active' => true,
'name' => [
[
'use' => 'official',
'family' => $patient->last_name,
'given' => [$patient->first_name]
]
],
'telecom' => [
[
'system' => 'phone',
'value' => $patient->mobile_number,
'use' => 'home'
],
[
'system' => 'email',
'value' => $patient->email,
'use' => 'home'
]
],
'gender' => strtolower($gender),
'birthDate' => Carbon::parse($patient->dob)->format('Y-m-d'),
'address' => [
[
'use' => 'home',
'line' => [$patient->address],
// 'city' => $this->faker->city,
// 'state' => $this->faker->stateAbbr,
'postalCode' => $patient->post_code,
'country' => $patient->country_id->iso2
]
]
];
}
public function generateCondition($patientId, $diagnosis)
{
return [
'resourceType' => 'Condition',
'clinicalStatus' => [
'coding' => [
[
'system' => 'http://terminology.hl7.org/CodeSystem/condition-clinical',
'code' => 'active',
]
]
],
'verificationStatus' => [
'coding' => [
[
'system' => 'http://terminology.hl7.org/CodeSystem/condition-ver-status',
'code' => 'confirmed',
]
]
],
'category' => [
[
'coding' => [
[
'system' => 'http://terminology.hl7.org/CodeSystem/condition-category',
'code' => 'problem-list-item',
'display' => 'Problem List Item'
]
]
]
],
'code' => [
'coding' => [
[
'system' => 'http://snomed.info/sct',
// 'code' => $condition['code'],
'display' => $diagnosis
]
],
'text' => $diagnosis
],
'subject' => [
'reference' => "Patient/$patientId"
],
'onsetDateTime' => Carbon::now()->format('Y-m-d'),
'recordedDate' => Carbon::now()->format('Y-m-d')
];
}
public function generateMedicationRequest($patientId, $prescriptions)
{
return [
'resourceType' => 'MedicationRequest',
'status' => 'active',
'intent' => 'order',
'medicationCodeableConcept' => [
'coding' => [
[
'system' => 'http://www.nlm.nih.gov/research/umls/rxnorm',
// 'code' => $medication['code'],
'display' => $prescriptions
]
]
],
'subject' => [
'reference' => "Patient/$patientId"
],
// 'requester' => [
// 'reference' => "Practitioner/$practitionerId"
// ],
'authoredOn' => Carbon::now()->format('Y-m-d'),
'dosageInstruction' => [
[
'text' => $prescriptions,
// 'timing' => [
// 'repeat' => [
// 'frequency' => $this->faker->numberBetween(1, 3),
// 'period' => 1,
// 'periodUnit' => 'd'
// ]
// ],
// 'route' => [
// 'coding' => [
// [
// 'system' => 'http://snomed.info/sct',
// 'code' => '26643006',
// 'display' => 'Oral route'
// ]
// ]
// ]
]
]
];
}
public function generateObservation($patientId, $observation)
{
return [
'resourceType' => 'Observation',
'status' => 'final',
'code' => [
'text' => $observation->health_profile_master->measurement_name
],
'valueQuantity' => [
'value' => $observation->measurement_val,
'unit' => $observation->health_profile_master->units
],
'subject' => [
'reference' => "Patient/$patientId"
],
'effectiveDateTime' => Carbon::now()->format('Y-m-d'),
'issued' => Carbon::now()->format('Y-m-d')
];
}
public function generateAllergy($patientId, $allergy)
{
return [
'resourceType' => 'AllergyIntolerance',
'clinicalStatus' => [
'coding' => [
[
'system' => 'http://terminology.hl7.org/CodeSystem/allergyintolerance-clinical',
'code' => 'active'
]
]
],
'verificationStatus' => [
'coding' => [
[
'system' => 'http://terminology.hl7.org/CodeSystem/allergyintolerance-verification',
'code' => 'confirmed'
]
]
],
'type' => 'allergy',
'category' => ['food', 'medication', 'environment'][rand(0, 2)],
'criticality' => 'unable-to-assess',
'code' => [
'coding' => [
[
'system' => 'http://snomed.info/sct',
// 'code' => $allergy['code'],
'display' => $allergy
]
]
],
'patient' => [
'reference' => "Patient/$patientId"
],
'onsetDateTime' => Carbon::now()->format('Y-m-d'),
'recordedDate' => Carbon::now()->format('Y-m-d')
];
}
public function generateProcedure($patientId, $procedure)
{
return [
'resourceType' => 'Procedure',
'code' => [
'text' => $procedure
],
'status' => 'completed',
'performedDateTime' => Carbon::now()->toDateString(),
'subject' => [
'reference' => "Patient/$patientId"
]
];
}
}