fix: standardize patch updates

This commit is contained in:
mahdahar 2026-04-08 08:37:41 +07:00
parent 945ea6d183
commit 84c81fe9c5
27 changed files with 884 additions and 468 deletions

View File

@ -19,12 +19,13 @@ $routes->group('api', ['filter' => 'auth'], function ($routes) {
$routes->get('audit-logs', 'Audit\AuditLogController::index');
// Results CRUD
$routes->group('result', function ($routes) {
$routes->get('/', 'ResultController::index');
$routes->get('(:num)', 'ResultController::show/$1');
$routes->group('result', function ($routes) {
$routes->get('/', 'ResultController::index');
$routes->post('/', 'ResultController::create');
$routes->get('(:num)', 'ResultController::show/$1');
$routes->patch('(:any)', 'ResultController::update/$1');
$routes->delete('(:num)', 'ResultController::delete/$1');
});
$routes->delete('(:num)', 'ResultController::delete/$1');
});
// Reports
$routes->get('report/(:num)', 'ReportController::view/$1');

View File

@ -1,13 +1,15 @@
<?php
namespace App\Controllers\Contact;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Contact\ContactModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Contact\ContactModel;
class ContactController extends BaseController {
use ResponseTrait;
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -78,16 +80,23 @@ class ContactController extends BaseController {
}
public function update($ContactID = null) {
$input = $this->request->getJSON(true);
if (!$ContactID || !ctype_digit((string) $ContactID)) {
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($ContactID, 'ContactID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([
'status' => 'failed',
'message' => 'ContactID is required and must be a valid integer',
'message' => 'Contact not found',
'data' => []
], 400);
}
if (empty($input) || !is_array($input)) {
return $this->failValidationErrors('No data provided for update.');
], 404);
}
$validationInput = array_intersect_key($input, $this->patchRules);
@ -95,13 +104,13 @@ class ContactController extends BaseController {
return $this->failValidationErrors($this->validator->getErrors());
}
$input['ContactID'] = (int) $ContactID;
$input['ContactID'] = $id;
try {
$this->model->saveContact($input);
$id = $input['ContactID'];
return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -1,12 +1,14 @@
<?php
namespace App\Controllers;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Location\LocationModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Location\LocationModel;
class LocationController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $model;
protected $rules;
@ -19,8 +21,21 @@ class LocationController extends BaseController {
'LocFull' => 'required',
];
$this->patchRules = [
'SiteID' => 'permit_empty|is_natural_no_zero',
'LocCode' => 'permit_empty|max_length[6]',
'Parent' => 'permit_empty|is_natural',
'LocFull' => 'permit_empty',
'Description' => 'permit_empty|max_length[255]',
'LocType' => 'permit_empty',
'Street1' => 'permit_empty|max_length[255]',
'Street2' => 'permit_empty|max_length[255]',
'City' => 'permit_empty|max_length[255]',
'Province' => 'permit_empty|max_length[255]',
'PostCode' => 'permit_empty|max_length[20]',
'GeoLocationSystem' => 'permit_empty|max_length[255]',
'GeoLocationData' => 'permit_empty|max_length[255]',
'Phone' => 'permit_empty|max_length[20]',
'Email' => 'permit_empty|valid_email|max_length[255]',
];
}
@ -56,30 +71,45 @@ class LocationController extends BaseController {
}
public function update($LocationID = null) {
$input = $this->request->getJSON(true);
if (!$LocationID || !ctype_digit((string) $LocationID)) {
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($LocationID, 'LocationID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([
'status' => 'failed',
'message' => 'LocationID is required and must be a valid integer',
'message' => 'Location not found',
'data' => []
], 400);
}
if (empty($input) || !is_array($input)) {
return $this->failValidationErrors('No data provided for update.');
], 404);
}
$validationInput = array_intersect_key($input, $this->patchRules);
if (!empty($validationInput) && !$this->validateData($validationInput, $this->patchRules)) {
if ($validationInput === []) {
return $this->respond([
'status' => 'failed',
'message' => 'No valid fields provided for update.',
'data' => []
], 422);
}
if (!$this->validateData($validationInput, $this->patchRules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
$input['LocationID'] = (int) $LocationID;
$input['LocationID'] = $id;
try {
$result = $this->model->saveLocation($input, true);
return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $result ], 201);
$result = $this->model->saveLocation($input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $result ], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function delete() {

View File

@ -1,13 +1,15 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\AccountModel;
use App\Models\Organization\AccountModel;
class AccountController extends BaseController {
use ResponseTrait;
class AccountController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -66,18 +68,27 @@ class AccountController extends BaseController {
}
public function update($AccountID = null) {
$input = $this->request->getJSON(true);
if (!$AccountID || !ctype_digit((string) $AccountID)) {
return $this->failValidationErrors('ID is required.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$input['AccountID'] = (int) $AccountID;
$id = $this->requirePatchId($AccountID, 'AccountID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Account not found', 'data' => [] ], 404);
}
$input['AccountID'] = $id;
try {
$id = $input['AccountID'];
if (!$id) { return $this->failValidationErrors('ID is required.'); }
$this->model->update($id, $input);
return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}
$this->model->update($id, $input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\CodingSysModel;
class CodingSysController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -79,22 +81,30 @@ class CodingSysController extends BaseController {
}
public function update($CodingSysID = null) {
$input = $this->request->getJSON(true);
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($CodingSysID, 'CodingSysID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond(['status' => 'failed', 'message' => 'CodingSys not found', 'data' => []], 404);
}
if (isset($input['CodingSysID']) && (string) $input['CodingSysID'] !== (string) $id) {
return $this->failValidationErrors('CodingSysID in URL does not match body.');
}
$input['CodingSysID'] = $id;
try {
if (!$CodingSysID || !ctype_digit((string) $CodingSysID)) {
return $this->failValidationErrors('CodingSysID is required.');
}
if (isset($input['CodingSysID']) && (string) $input['CodingSysID'] !== (string) $CodingSysID) {
return $this->failValidationErrors('CodingSysID in URL does not match body.');
}
$id = (int) $CodingSysID;
$input['CodingSysID'] = $id;
$this->model->update($id, $input);
return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201);
return $this->respond(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}

View File

@ -1,13 +1,15 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\DepartmentModel;
use App\Models\Organization\DepartmentModel;
class DepartmentController extends BaseController {
use ResponseTrait;
class DepartmentController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -63,17 +65,27 @@ class DepartmentController extends BaseController {
}
public function update($DepartmentID = null) {
$input = $this->request->getJSON(true);
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($DepartmentID, 'DepartmentID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Department not found', 'data' => [] ], 404);
}
$input['DepartmentID'] = $id;
try {
if (!$DepartmentID || !ctype_digit((string) $DepartmentID)) {
return $this->failValidationErrors('ID is required.');
}
$input['DepartmentID'] = (int) $DepartmentID;
$id = $input['DepartmentID'];
$this->model->update($id, $input);
return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -1,13 +1,15 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\DisciplineModel;
use App\Models\Organization\DisciplineModel;
class DisciplineController extends BaseController {
use ResponseTrait;
class DisciplineController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -64,12 +66,24 @@ class DisciplineController extends BaseController {
}
public function update($DisciplineID = null) {
$input = $this->request->getJSON(true);
if (!$DisciplineID || !ctype_digit((string) $DisciplineID)) { return $this->failValidationErrors('ID is required.'); }
$input['DisciplineID'] = (int) $DisciplineID;
$id = $input['DisciplineID'];
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($DisciplineID, 'DisciplineID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Discipline not found', 'data' => [] ], 404);
}
$input['DisciplineID'] = $id;
$this->model->update($id, $input);
return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
/*
try {
$id = $input['DisciplineID'];

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\HostAppModel;
class HostAppController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -83,21 +85,29 @@ class HostAppController extends BaseController {
}
public function update($HostAppID = null) {
$input = $this->request->getJSON(true);
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($HostAppID, 'HostAppID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond(['status' => 'failed', 'message' => 'HostApp not found', 'data' => []], 404);
}
if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $id) {
return $this->failValidationErrors('HostAppID in URL does not match body.');
}
$input['HostAppID'] = $id;
try {
if ($HostAppID === null || $HostAppID === '') {
return $this->failValidationErrors('HostAppID is required.');
}
if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $HostAppID) {
return $this->failValidationErrors('HostAppID in URL does not match body.');
}
$id = $HostAppID;
$input['HostAppID'] = $id;
$this->model->update($id, $input);
return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201);
return $this->respond(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\HostComParaModel;
class HostComParaController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -83,22 +85,30 @@ class HostComParaController extends BaseController {
}
public function update($HostAppID = null) {
$input = $this->request->getJSON(true);
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($HostAppID, 'HostAppID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond(['status' => 'failed', 'message' => 'HostComPara not found', 'data' => []], 404);
}
if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $id) {
return $this->failValidationErrors('HostAppID in URL does not match body.');
}
$input['HostAppID'] = $id;
try {
if ($HostAppID === null || $HostAppID === '') {
return $this->failValidationErrors('HostAppID is required.');
}
if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $HostAppID) {
return $this->failValidationErrors('HostAppID in URL does not match body.');
}
$id = $HostAppID;
$input['HostAppID'] = $id;
$this->model->update($id, $input);
return $this->respondCreated(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 201);
return $this->respond(['status' => 'success', 'message' => 'data updated successfully', 'data' => $id], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}

View File

@ -1,13 +1,15 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\SiteModel;
use App\Models\Organization\SiteModel;
class SiteController extends BaseController {
use ResponseTrait;
class SiteController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -76,13 +78,23 @@ class SiteController extends BaseController {
}
public function update($SiteID = null) {
$input = $this->request->getJSON(true);
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
if (!$SiteID || !ctype_digit((string) $SiteID)) { return $this->failValidationErrors('ID is required.'); }
$input['SiteID'] = (int) $SiteID;
$id = $input['SiteID'];
$id = $this->requirePatchId($SiteID, 'SiteID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Site not found', 'data' => [] ], 404);
}
$input['SiteID'] = $id;
if (!empty($input['SiteCode'])) {
$validation = service('validation');
$validation->setRules([
@ -94,11 +106,11 @@ class SiteController extends BaseController {
}
}
try {
$this->model->update($id, $input);
return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}
try {
$this->model->update($id, $input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -1,13 +1,15 @@
<?php
namespace App\Controllers\Organization;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Organization\WorkstationModel;
use App\Models\Organization\WorkstationModel;
class WorkstationController extends BaseController {
use ResponseTrait;
class WorkstationController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -64,17 +66,27 @@ class WorkstationController extends BaseController {
}
public function update($WorkstationID = null) {
$input = $this->request->getJSON(true);
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($WorkstationID, 'WorkstationID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Workstation not found', 'data' => [] ], 404);
}
$input['WorkstationID'] = $id;
try {
if (!$WorkstationID || !ctype_digit((string) $WorkstationID)) {
return $this->failValidationErrors('ID is required.');
}
$input['WorkstationID'] = (int) $WorkstationID;
$id = $input['WorkstationID'];
$this->model->update($id, $input);
return $this->respondCreated([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -1,14 +1,16 @@
<?php
namespace App\Controllers;
use App\Traits\ResponseTrait;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\PatVisit\PatVisitModel;
use App\Models\PatVisit\PatVisitADTModel;
use App\Models\Patient\PatientModel;
class PatVisitController extends BaseController {
use ResponseTrait;
class PatVisitController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $model;
@ -95,24 +97,28 @@ class PatVisitController extends BaseController {
}
public function update($InternalPVID = null) {
$input = $this->request->getJSON(true);
if (!$InternalPVID || !is_numeric($InternalPVID)) {
return $this->respond(['status' => 'error', 'message' => 'Invalid or missing ID'], 400);
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$input['InternalPVID'] = $InternalPVID;
$id = $this->requirePatchId($InternalPVID, 'InternalPVID');
if ($id === null) {
return;
}
$visit = $this->model->find($id);
if (!$visit) {
return $this->respond(['status' => 'failed', 'message' => 'Visit not found', 'data' => []], 404);
}
$input['InternalPVID'] = $id;
try {
// Check if visit exists
$visit = $this->model->find($input["InternalPVID"]);
if (!$visit) {
return $this->respond(['status' => 'error', 'message' => 'Visit not found'], 404);
}
$data = $this->model->updatePatVisit($input);
return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
$data = $this->model->updatePatVisit($input);
return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function create() {
$input = $this->request->getJSON(true);
@ -175,17 +181,34 @@ class PatVisitController extends BaseController {
}
public function updateADT($PVADTID = null) {
$input = $this->request->getJSON(true);
if (!$PVADTID || !is_numeric($PVADTID)) { return $this->respond(['status' => 'error', 'message' => 'Invalid or missing ID'], 400); }
$input['PVADTID'] = $PVADTID;
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = $this->requirePatchId($PVADTID, 'PVADTID');
if ($id === null) {
return;
}
$modelPVA = new PatVisitADTModel();
$adt = $modelPVA->find($id);
if (!$adt) {
return $this->respond(['status' => 'failed', 'message' => 'ADT record not found', 'data' => []], 404);
}
if (!isset($input['InternalPVID'])) {
$input['InternalPVID'] = $adt['InternalPVID'];
}
$input['PVADTID'] = $id;
try {
$data = $modelPVA->update($input['PVADTID'], $input);
return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
$data = $modelPVA->update($id, $input);
return $this->respond(['status' => 'success', 'message' => 'Data updated successfully', 'data' => $data], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function getADTByVisit($InternalPVID = null) {
try {

View File

@ -2,12 +2,14 @@
namespace App\Controllers;
use App\Traits\ResponseTrait;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use CodeIgniter\Controller;
use App\Models\PatResultModel;
class ResultController extends Controller {
use ResponseTrait;
class ResultController extends Controller {
use ResponseTrait;
use PatchValidationTrait;
protected $model;
@ -24,24 +26,28 @@ class ResultController extends Controller {
$orderID = $this->request->getGet('order_id');
$patientID = $this->request->getGet('patient_id');
if ($orderID) {
$results = $this->model->getByOrder((int)$orderID);
} elseif ($patientID) {
$results = $this->model->getByPatient((int)$patientID);
} else {
// Get all results with pagination
$page = (int)($this->request->getGet('page') ?? 1);
$perPage = (int)($this->request->getGet('per_page') ?? 20);
$results = $this->model
->where('DelDate', null)
->orderBy('ResultID', 'DESC')
->paginate($perPage, 'default', $page);
}
return $this->respond([
'status' => 'success',
'message' => 'Results retrieved successfully',
if ($orderID) {
$results = $this->model->getByOrder((int)$orderID);
} elseif ($patientID) {
$results = $this->model->getByPatient((int)$patientID);
} else {
// Get all results with pagination
$page = (int)($this->request->getGet('page') ?? 1);
$perPage = (int)($this->request->getGet('per_page') ?? 20);
$results = $this->model
->where('DelDate', null)
->orderBy('ResultID', 'DESC')
->paginate($perPage, 'default', $page);
}
$results = is_array($results)
? array_map([$this, 'hydrateResultPayload'], $results)
: $results;
return $this->respond([
'status' => 'success',
'message' => 'Results retrieved successfully',
'data' => $results
], 200);
@ -59,51 +65,110 @@ class ResultController extends Controller {
* Get single result
* GET /api/result/{id}
*/
public function show($id) {
try {
$result = $this->model->getWithRelations((int)$id);
if (!$result) {
return $this->respond([
'status' => 'failed',
'message' => 'Result not found',
'data' => []
], 404);
}
return $this->respond([
'status' => 'success',
'message' => 'Result retrieved successfully',
'data' => $result
], 200);
} catch (\Exception $e) {
log_message('error', 'ResultController::show error: ' . $e->getMessage());
return $this->respond([
'status' => 'failed',
'message' => 'Failed to retrieve result',
'data' => []
], 500);
}
}
public function show($id) {
try {
$result = $this->model->getWithRelations((int)$id);
if (!$result) {
return $this->respond([
'status' => 'failed',
'message' => 'Result not found',
'data' => []
], 404);
}
$result = $this->hydrateResultPayload($result);
return $this->respond([
'status' => 'success',
'message' => 'Result retrieved successfully',
'data' => $result
], 200);
} catch (\Exception $e) {
log_message('error', 'ResultController::show error: ' . $e->getMessage());
return $this->respond([
'status' => 'failed',
'message' => 'Failed to retrieve result',
'data' => []
], 500);
}
}
/**
* Create a new result entry
* POST /api/result
*/
public function create() {
$payload = $this->request->getJSON(true);
if (!is_array($payload) || empty($payload)) {
return $this->respond([
'status' => 'failed',
'message' => 'No data provided',
'data' => []
], 400);
}
if (isset($payload['ResultValue'])) {
$payload['Result'] = $payload['ResultValue'];
}
try {
$resultId = $this->model->insert($payload, true);
if (!$resultId) {
return $this->respond([
'status' => 'failed',
'message' => 'Failed to create result',
'data' => []
], 500);
}
return $this->respondCreated([
'status' => 'success',
'message' => 'Result created successfully',
'data' => [
'ResultID' => $resultId,
'ResultValue' => $payload['ResultValue'] ?? ($payload['Result'] ?? null),
'ResultCode' => $payload['ResultCode'] ?? null,
]
], 201);
} catch (\Exception $e) {
log_message('error', 'ResultController::create error: ' . $e->getMessage());
return $this->respond([
'status' => 'failed',
'message' => 'Failed to create result',
'data' => []
], 500);
}
}
/**
* Update result with validation
* PATCH /api/result/{id}
*/
public function update($id) {
try {
$data = $this->request->getJSON(true);
if (empty($data)) {
return $this->respond([
'status' => 'failed',
'message' => 'No data provided',
'data' => []
], 400);
}
$result = $this->model->updateWithValidation((int)$id, $data);
public function update($id) {
try {
$data = $this->requirePatchPayload($this->request->getJSON(true));
if ($data === null) {
return;
}
$validatedId = $this->requirePatchId($id, 'ResultID');
if ($validatedId === null) {
return;
}
$existing = $this->model->find($validatedId);
if (!$existing) {
return $this->respond([
'status' => 'failed',
'message' => 'Result not found',
'data' => []
], 404);
}
$result = $this->model->updateWithValidation($validatedId, $data);
if (!$result['success']) {
return $this->respond([
@ -114,16 +179,16 @@ class ResultController extends Controller {
}
// Get updated result with relations
$updatedResult = $this->model->getWithRelations((int)$id);
return $this->respond([
'status' => 'success',
'message' => $result['message'],
'data' => [
'result' => $updatedResult,
'flag' => $result['flag']
]
], 200);
$updatedResult = $this->model->getWithRelations($validatedId);
return $this->respond([
'status' => 'success',
'message' => $result['message'],
'data' => [
'result' => $updatedResult ? $this->hydrateResultPayload($updatedResult) : [],
'flag' => $result['flag']
]
], 200);
} catch (\Exception $e) {
log_message('error', 'ResultController::update error: ' . $e->getMessage());
@ -139,7 +204,7 @@ class ResultController extends Controller {
* Soft delete result
* DELETE /api/result/{id}
*/
public function delete($id) {
public function delete($id) {
try {
$result = $this->model->find((int)$id);
@ -174,6 +239,13 @@ class ResultController extends Controller {
'message' => 'Failed to delete result',
'data' => []
], 500);
}
}
}
}
}
private function hydrateResultPayload(array $payload): array {
if (!array_key_exists('ResultValue', $payload) && array_key_exists('Result', $payload)) {
$payload['ResultValue'] = $payload['Result'];
}
return $payload;
}
}

View File

@ -6,11 +6,13 @@ use App\Controllers\BaseController;
use App\Models\Rule\RuleDefModel;
use App\Models\Test\TestDefSiteModel;
use App\Services\RuleExpressionService;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
class RuleController extends BaseController
{
class RuleController extends BaseController
{
use ResponseTrait;
use PatchValidationTrait;
protected RuleDefModel $ruleDefModel;
@ -98,7 +100,10 @@ class RuleController extends BaseController
public function create()
{
$input = $this->request->getJSON(true) ?? [];
$input = $this->requirePatchPayload($this->request->getJSON(true) ?? []);
if ($input === null) {
return;
}
$validation = service('validation');
$validation->setRules([

View File

@ -2,13 +2,15 @@
namespace App\Controllers\Specimen;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\ContainerDefModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\ContainerDefModel;
class ContainerDefController extends BaseController {
use ResponseTrait;
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -79,13 +81,19 @@ class ContainerDefController extends BaseController {
}
public function update($ConDefID = null) {
$input = $this->request->getJSON(true);
if (!$ConDefID || !ctype_digit((string) $ConDefID)) {
return $this->failValidationErrors('ConDefID is required.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
if (empty($input) || !is_array($input)) {
return $this->failValidationErrors('No data provided for update.');
$id = $this->requirePatchId($ConDefID, 'ConDefID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Container definition not found', 'data' => [] ], 404);
}
$validationInput = array_intersect_key($input, $this->patchRules);
@ -93,13 +101,13 @@ class ContainerDefController extends BaseController {
return $this->failValidationErrors($this->validator->getErrors());
}
$input['ConDefID'] = (int) $ConDefID;
$input['ConDefID'] = $id;
try {
$ConDefID = $this->model->update($input['ConDefID'], $input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data $ConDefID updated successfully" ]);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
$this->model->update($id, $input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -2,13 +2,15 @@
namespace App\Controllers\Specimen;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenCollectionModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenCollectionModel;
class SpecimenCollectionController extends BaseController {
use ResponseTrait;
class SpecimenCollectionController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -67,18 +69,29 @@ class SpecimenCollectionController extends BaseController {
}
public function update($SpcColID = null) {
$input = $this->request->getJSON(true);
if (!$SpcColID || !ctype_digit((string) $SpcColID)) {
return $this->failValidationErrors('SpcColID is required.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$input['SpcColID'] = (int) $SpcColID;
$id = $this->requirePatchId($SpcColID, 'SpcColID');
if ($id === null) {
return;
}
$existing = $this->model->where('SpcColID', $id)->first();
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Specimen collection not found', 'data' => [] ], 404);
}
$input['SpcColID'] = $id;
if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
try {
$id = $this->model->update($input['SpcColID'], $input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
$this->model->update($id, $input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -2,13 +2,15 @@
namespace App\Controllers\Specimen;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenModel;
class SpecimenController extends BaseController {
use ResponseTrait;
class SpecimenController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -67,19 +69,30 @@ class SpecimenController extends BaseController {
}
public function update($SID = null) {
$input = $this->request->getJSON(true);
if (!$SID || !ctype_digit((string) $SID)) {
return $this->failValidationErrors('SID is required.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$input['SID'] = (int) $SID;
$id = $this->requirePatchId($SID, 'SID');
if ($id === null) {
return;
}
$existing = $this->model->where('SID', $id)->first();
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Specimen not found', 'data' => [] ], 404);
}
$input['SID'] = $id;
if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
try {
$id = $this->model->update($input['SID'], $input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
$this->model->update($id, $input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
/**
* Delete a specimen (soft delete)

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Specimen;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Specimen\SpecimenPrepModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Models\Specimen\SpecimenPrepModel;
class SpecimenPrepController extends BaseController {
use ResponseTrait;
class SpecimenPrepController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -52,18 +54,29 @@ class SpecimenPrepController extends BaseController {
}
public function update($SpcPrpID = null) {
$input = $this->request->getJSON(true);
if (!$SpcPrpID || !ctype_digit((string) $SpcPrpID)) {
return $this->failValidationErrors('SpcPrpID is required.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$input['SpcPrpID'] = (int) $SpcPrpID;
$id = $this->requirePatchId($SpcPrpID, 'SpcPrpID');
if ($id === null) {
return;
}
$existing = $this->model->where('SpcPrpID', $id)->first();
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Specimen prep not found', 'data' => [] ], 404);
}
$input['SpcPrpID'] = $id;
if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
try {
$id = $this->model->update($input['SpcPrpID'], $input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
$this->model->update($id, $input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -2,13 +2,15 @@
namespace App\Controllers\Specimen;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenStatusModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Specimen\SpecimenStatusModel;
class SpecimenStatusController extends BaseController {
use ResponseTrait;
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $model;
@ -65,18 +67,29 @@ class SpecimenStatusController extends BaseController {
}
public function update($SpcStaID = null) {
$input = $this->request->getJSON(true);
if (!$SpcStaID || !ctype_digit((string) $SpcStaID)) {
return $this->failValidationErrors('SpcStaID is required.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$input['SpcStaID'] = (int) $SpcStaID;
$id = $this->requirePatchId($SpcStaID, 'SpcStaID');
if ($id === null) {
return;
}
$existing = $this->model->where('SpcStaID', $id)->first();
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Specimen status not found', 'data' => [] ], 404);
}
$input['SpcStaID'] = $id;
if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
try {
$id = $this->model->update($input['SpcStaID'], $input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
$this->model->update($id, $input);
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
}

View File

@ -1,14 +1,16 @@
<?php
namespace App\Controllers\Test;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Test\TestMapModel;
use App\Models\Test\TestMapDetailModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\ValueSet;
use App\Models\Test\TestMapModel;
use App\Models\Test\TestMapDetailModel;
class TestMapController extends BaseController {
use ResponseTrait;
class TestMapController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $rules;
@ -73,12 +75,21 @@ class TestMapController extends BaseController {
}
public function update($TestMapID = null) {
$input = $this->request->getJSON(true);
if (!$TestMapID || !ctype_digit((string) $TestMapID)) { return $this->failValidationErrors('TestMapID is required.'); }
if (empty($input) || !is_array($input)) {
return $this->failValidationErrors('No data provided for update.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
$id = (int) $TestMapID;
$id = $this->requirePatchId($TestMapID, 'TestMapID');
if ($id === null) {
return;
}
$existing = $this->model->where('TestMapID', $id)->where('EndDate', null)->first();
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Test map not found', 'data' => [] ], 404);
}
if (isset($input['TestMapID']) && (string) $input['TestMapID'] !== (string) $id) {
return $this->failValidationErrors('TestMapID in URL does not match body.');
}
@ -91,10 +102,10 @@ class TestMapController extends BaseController {
$input['TestMapID'] = $id;
try {
$this->model->update($id,$input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data updated successfully", 'data' => $id ]);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function delete() {

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Test;
use App\Controllers\BaseController;
use App\Traits\ResponseTrait;
use App\Models\Test\TestMapDetailModel;
use App\Controllers\BaseController;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
use App\Models\Test\TestMapDetailModel;
class TestMapDetailController extends BaseController {
use ResponseTrait;
class TestMapDetailController extends BaseController {
use ResponseTrait;
use PatchValidationTrait;
protected $db;
protected $rules;
@ -99,35 +101,42 @@ class TestMapDetailController extends BaseController {
}
public function update($TestMapDetailID = null) {
$input = $this->request->getJSON(true);
if (!$TestMapDetailID || !ctype_digit((string) $TestMapDetailID)) {
return $this->failValidationErrors('TestMapDetailID is required.');
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
if (empty($input) || !is_array($input)) {
return $this->failValidationErrors('No data provided for update.');
$id = $this->requirePatchId($TestMapDetailID, 'TestMapDetailID');
if ($id === null) {
return;
}
$id = (int) $TestMapDetailID;
$existing = $this->model->where('TestMapDetailID', $id)->where('EndDate', null)->first();
if (!$existing) {
return $this->respond([ 'status' => 'failed', 'message' => 'Test map detail not found', 'data' => [] ], 404);
}
if (isset($input['TestMapDetailID']) && (string) $input['TestMapDetailID'] !== (string) $id) {
return $this->failValidationErrors('TestMapDetailID in URL does not match body.');
}
$input['TestMapDetailID'] = $id;
$validationInput = array_intersect_key($input, $this->patchRules);
if (!empty($validationInput) && !$this->validateData($validationInput, $this->patchRules)) {
return $this->failValidationErrors($this->validator->getErrors());
}
try {
$this->model->update($id, $input);
return $this->respond([
'status' => 'success',
'message' => "data updated successfully",
'data' => $id
], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
try {
$this->model->update($id, $input);
return $this->respond([
'status' => 'success',
'message' => 'data updated successfully',
'data' => $id
], 200);
} catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function delete() {
$input = $this->request->getJSON(true);

View File

@ -2,14 +2,16 @@
namespace App\Controllers\Test;
use App\Controllers\BaseController;
use App\Libraries\TestValidationService;
use App\Libraries\ValueSet;
use App\Traits\ResponseTrait;
use App\Controllers\BaseController;
use App\Libraries\TestValidationService;
use App\Libraries\ValueSet;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
class TestsController extends BaseController
{
use ResponseTrait;
class TestsController extends BaseController
{
use ResponseTrait;
use PatchValidationTrait;
protected $model;
protected $modelCal;
@ -205,21 +207,30 @@ class TestsController extends BaseController
}
}
public function update($id = null)
{
$input = $this->request->getJSON(true);
if (!$id && isset($input['TestSiteID'])) {
$id = $input['TestSiteID'];
}
if (!$id) {
return $this->failValidationErrors('TestSiteID is required.');
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->failNotFound('Test not found');
}
public function update($id = null)
{
$input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
return;
}
if (!$id && isset($input['TestSiteID'])) {
$id = $input['TestSiteID'];
}
$id = $this->requirePatchId($id, 'TestSiteID');
if ($id === null) {
return;
}
$existing = $this->model->find($id);
if (!$existing) {
return $this->respond([
'status' => 'failed',
'message' => 'Test not found',
'data' => []
], 404);
}
$testType = $input['TestType'] ?? $existing['TestType'] ?? '';
$details = $input['details'] ?? $input;

View File

@ -4,6 +4,7 @@ namespace App\Controllers\User;
use App\Controllers\BaseController;
use App\Models\User\UserModel;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait;
/**
@ -13,6 +14,7 @@ use App\Traits\ResponseTrait;
class UserController extends BaseController
{
use ResponseTrait;
use PatchValidationTrait;
protected $model;
protected $db;
@ -178,7 +180,10 @@ class UserController extends BaseController
public function update($id)
{
try {
$data = $this->request->getJSON(true);
$data = $this->requirePatchPayload($this->request->getJSON(true));
if ($data === null) {
return;
}
if (empty($id) || !ctype_digit((string) $id)) {
return $this->respond([

View File

@ -1,21 +1,25 @@
<?php
namespace App\Models\Location;
use App\Models\BaseModel;
use App\Libraries\ValueSet;
class LocationModel extends BaseModel {
protected $table = 'location';
namespace App\Models\Location;
use App\Libraries\ValueSet;
use App\Models\BaseModel;
use App\Models\Location\LocationAddressModel;
class LocationModel extends BaseModel {
protected $table = 'location';
protected $primaryKey = 'LocationID';
protected $allowedFields = ['SiteID', 'LocCode', 'Parent', 'LocFull', 'Description', 'LocType', 'CreateDate', 'EndDate'];
protected $useTimestamps = true;
protected $allowedFields = ['SiteID', 'LocCode', 'Parent', 'LocFull', 'Description', 'LocType', 'CreateDate', 'EndDate'];
private array $locationEditableFields = ['SiteID', 'LocCode', 'Parent', 'LocFull', 'Description', 'LocType'];
private array $addressEditableFields = ['Street1', 'Street2', 'City', 'Province', 'PostCode', 'GeoLocationSystem', 'GeoLocationData', 'Phone', 'Email'];
protected $useTimestamps = true;
protected $createdField = 'CreateDate';
protected $updatedField = '';
protected $useSoftDeletes = true;
protected $deletedField = 'EndDate';
public function getLocations($LocCode, $LocName) {
$sql = $this->select("LocationID, LocCode, Parent, LocFull, LocType");
public function getLocations($LocCode, $LocName) {
$sql = $this->select("LocationID, LocCode, Parent, LocFull, LocType");
if($LocName != '') { $sql->like('LocFull', $LocName, 'both'); }
if($LocCode != '') { $sql->like('LocCode', $LocCode, 'both'); }
$rows = $sql->findAll();
@ -25,9 +29,9 @@ class LocationModel extends BaseModel {
return $rows;
}
public function getLocation($LocationID) {
$row = $this->select("location.*, la.Street1, la.Street2, la.PostCode, la.GeoLocationSystem, la.GeoLocationData,
la.Province as Province, prop.AreaName as ProvinceLabel, la.City as City, city.AreaName as CityLabel, site.SiteID, site.SiteName")
public function getLocation($LocationID) {
$row = $this->select("location.*, la.Street1, la.Street2, la.PostCode, la.GeoLocationSystem, la.GeoLocationData,
la.Province as Province, prop.AreaName as ProvinceLabel, la.City as City, city.AreaName as CityLabel, site.SiteID, site.SiteName")
->join("locationaddress la", "location.LocationID=la.LocationID", "left")
->join("areageo prop", "la.Province=prop.AreaGeoID", "left")
->join("areageo city", "la.City=city.AreaGeoID", "left")
@ -40,29 +44,69 @@ class LocationModel extends BaseModel {
'LocType' => 'location_type',
])[0];
return $row;
}
return $row;
}
private function extractLocationFields(array $data): array
{
return $this->filterFields($data, $this->locationEditableFields);
}
private function extractAddressFields(array $data): array
{
return $this->filterFields($data, $this->addressEditableFields);
}
private function filterFields(array $data, array $fields): array
{
return array_intersect_key($data, array_flip($fields));
}
private function persistAddressFields(int $LocationID, array $data, LocationAddressModel $modelAddress, bool $forceInsert = false): void
{
$addressPayload = $this->extractAddressFields($data);
if ($addressPayload === [] && !$forceInsert) {
return;
}
$payloadWithId = array_merge(['LocationID' => $LocationID], $addressPayload);
if ($forceInsert) {
$modelAddress->insert($payloadWithId);
return;
}
$existingAddress = $modelAddress->find($LocationID);
if ($existingAddress === null) {
$modelAddress->insert($payloadWithId);
return;
}
$modelAddress->update($LocationID, $addressPayload);
}
public function saveLocation(array $data): array {
$modelAddress = new \App\Models\Location\LocationAddressModel();
$db = \Config\Database::connect();
$db->transBegin();
try {
if (!empty($data['LocationID'])) {
$LocationID = $data['LocationID'];
$this->update($LocationID, $data);
$modelAddress->update($LocationID, $data);
} else {
$LocationID = $this->insert($data, true);
$data['LocationID'] = $LocationID;
$modelAddress->insert($data);
}
if ($db->transStatus() === false) {
$error = $db->error();
$db->transRollback();
throw new \Exception($error['message'] ?? 'Transaction failed');
}
$db->transCommit();
public function saveLocation(array $data): array {
$modelAddress = new LocationAddressModel();
$db = \Config\Database::connect();
$db->transBegin();
try {
if (!empty($data['LocationID'])) {
$LocationID = $data['LocationID'];
$locationPayload = $this->extractLocationFields($data);
if ($locationPayload !== []) {
$this->update($LocationID, $locationPayload);
}
$this->persistAddressFields($LocationID, $data, $modelAddress);
} else {
$LocationID = $this->insert($data, true);
$this->persistAddressFields($LocationID, $data, $modelAddress, true);
}
if ($db->transStatus() === false) {
$error = $db->error();
$db->transRollback();
throw new \Exception($error['message'] ?? 'Transaction failed');
}
$db->transCommit();
return [ 'status' => 'success', 'LocationID' => $LocationID ];
} catch (\Throwable $e) {
$db->transRollback();

View File

@ -8,17 +8,19 @@ use App\Models\Patient\PatientModel;
class PatResultModel extends BaseModel {
protected $table = 'patres';
protected $primaryKey = 'ResultID';
protected $allowedFields = [
'SiteID',
'OrderID',
'InternalSID',
'SID',
'SampleID',
'TestSiteID',
'TestSiteCode',
'AspCnt',
'Result',
'SampleType',
protected $allowedFields = [
'SiteID',
'OrderID',
'InternalSID',
'SID',
'SampleID',
'TestSiteID',
'TestSiteCode',
'AspCnt',
'ResultCode',
'Result',
'ResultValue',
'SampleType',
'ResultDateTime',
'WorkstationID',
'EquipmentID',

View File

@ -0,0 +1,20 @@
<?php
namespace App\Traits;
trait PatchValidationTrait {
protected function requirePatchId(mixed $id, string $label = 'ID'): ?int {
if ($id === null || $id === '' || !ctype_digit((string) $id)) {
$this->failValidationErrors("{$label} is required and must be a valid integer.");
return null;
}
return (int) $id;
}
protected function requirePatchPayload(mixed $payload): ?array {
if (!is_array($payload) || empty($payload)) {
$this->failValidationErrors('No data provided for update.');
return null;
}
return $payload;
}
}

View File

@ -37,8 +37,8 @@ class LocationPatchTest extends CIUnitTestCase
private function createLocation(array $data = []): array
{
$payload = array_merge([
'LocationCode' => 'LOC_' . uniqid(),
'LocationName' => 'Test Location ' . uniqid(),
'LocCode' => 'LC' . substr(uniqid(), -4),
'LocFull' => 'Test Location ' . uniqid(),
], $data);
$response = $this->withHeaders($this->authHeaders())
@ -47,7 +47,12 @@ class LocationPatchTest extends CIUnitTestCase
$response->assertStatus(201);
$decoded = json_decode($response->getJSON(), true);
return $decoded['data'];
$locationId = $decoded['data']['LocationID'];
$show = $this->withHeaders($this->authHeaders())
->call('get', "{$this->endpoint}/{$locationId}");
$show->assertStatus(200);
$showData = json_decode($show->getJSON(), true)['data'];
return $showData;
}
public function testPartialUpdateLocationSuccess()
@ -57,7 +62,7 @@ class LocationPatchTest extends CIUnitTestCase
$patch = $this->withHeaders($this->authHeaders())
->withBodyFormat('json')
->call('patch', "{$this->endpoint}/{$id}", ['LocationName' => 'Updated Location']);
->call('patch', "{$this->endpoint}/{$id}", ['LocFull' => 'Updated Location']);
$patch->assertStatus(200);
$patchData = json_decode($patch->getJSON(), true);
@ -67,15 +72,15 @@ class LocationPatchTest extends CIUnitTestCase
$show->assertStatus(200);
$showData = json_decode($show->getJSON(), true)['data'];
$this->assertEquals('Updated Location', $showData['LocationName']);
$this->assertEquals($location['LocationCode'], $showData['LocationCode']);
$this->assertEquals('Updated Location', $showData['LocFull']);
$this->assertEquals($location['LocCode'], $showData['LocCode']);
}
public function testPartialUpdateLocationNotFound()
{
$patch = $this->withHeaders($this->authHeaders())
->withBodyFormat('json')
->call('patch', "{$this->endpoint}/999999", ['LocationName' => 'Updated']);
->call('patch', "{$this->endpoint}/999999", ['LocFull' => 'Updated']);
$patch->assertStatus(404);
}
@ -84,7 +89,7 @@ class LocationPatchTest extends CIUnitTestCase
{
$patch = $this->withHeaders($this->authHeaders())
->withBodyFormat('json')
->call('patch', "{$this->endpoint}/invalid", ['LocationName' => 'Updated']);
->call('patch', "{$this->endpoint}/invalid", ['LocFull' => 'Updated']);
$patch->assertStatus(400);
}
@ -108,14 +113,32 @@ class LocationPatchTest extends CIUnitTestCase
$patch = $this->withHeaders($this->authHeaders())
->withBodyFormat('json')
->call('patch', "{$this->endpoint}/{$id}", ['LocationCode' => 'NEW_CODE_' . uniqid()]);
->call('patch', "{$this->endpoint}/{$id}", ['LocCode' => 'LC' . substr(uniqid(), -4)]);
$patch->assertStatus(200);
$showData = json_decode($this->withHeaders($this->authHeaders())
->call('get', "{$this->endpoint}/{$id}")
->getJSON(), true)['data'];
$this->assertNotEquals($location['LocationCode'], $showData['LocationCode']);
$this->assertEquals($location['LocationName'], $showData['LocationName']);
$this->assertNotEquals($location['LocCode'], $showData['LocCode']);
$this->assertEquals($location['LocFull'], $showData['LocFull']);
}
public function testPartialUpdateLocationAddressField()
{
$location = $this->createLocation();
$id = $location['LocationID'];
$patch = $this->withHeaders($this->authHeaders())
->withBodyFormat('json')
->call('patch', "{$this->endpoint}/{$id}", ['Street1' => '123 Market St']);
$patch->assertStatus(200);
$showData = json_decode($this->withHeaders($this->authHeaders())
->call('get', "{$this->endpoint}/{$id}")
->getJSON(), true)['data'];
$this->assertEquals('123 Market St', $showData['Street1']);
}
}