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

@ -21,6 +21,7 @@ $routes->group('api', ['filter' => 'auth'], function ($routes) {
// Results CRUD // Results CRUD
$routes->group('result', function ($routes) { $routes->group('result', function ($routes) {
$routes->get('/', 'ResultController::index'); $routes->get('/', 'ResultController::index');
$routes->post('/', 'ResultController::create');
$routes->get('(:num)', 'ResultController::show/$1'); $routes->get('(:num)', 'ResultController::show/$1');
$routes->patch('(:any)', 'ResultController::update/$1'); $routes->patch('(:any)', 'ResultController::update/$1');
$routes->delete('(:num)', 'ResultController::delete/$1'); $routes->delete('(:num)', 'ResultController::delete/$1');

View File

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

View File

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

View File

@ -1,6 +1,7 @@
<?php <?php
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
@ -8,6 +9,7 @@ use App\Models\Organization\AccountModel;
class AccountController extends BaseController { class AccountController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -66,16 +68,25 @@ class AccountController extends BaseController {
} }
public function update($AccountID = null) { public function update($AccountID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if (!$AccountID || !ctype_digit((string) $AccountID)) { if ($input === null) {
return $this->failValidationErrors('ID is required.'); 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 { try {
$id = $input['AccountID'];
if (!$id) { return $this->failValidationErrors('ID is required.'); }
$this->model->update($id, $input); $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) { } catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Models\Organization\CodingSysModel; use App\Models\Organization\CodingSysModel;
class CodingSysController extends BaseController { class CodingSysController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -79,22 +81,30 @@ class CodingSysController extends BaseController {
} }
public function update($CodingSysID = null) { public function update($CodingSysID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
try { return;
if (!$CodingSysID || !ctype_digit((string) $CodingSysID)) {
return $this->failValidationErrors('CodingSysID is required.');
} }
if (isset($input['CodingSysID']) && (string) $input['CodingSysID'] !== (string) $CodingSysID) { $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.'); return $this->failValidationErrors('CodingSysID in URL does not match body.');
} }
$id = (int) $CodingSysID;
$input['CodingSysID'] = $id; $input['CodingSysID'] = $id;
try {
$this->model->update($id, $input); $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) { } catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

@ -1,6 +1,7 @@
<?php <?php
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
@ -8,6 +9,7 @@ use App\Models\Organization\DepartmentModel;
class DepartmentController extends BaseController { class DepartmentController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -63,15 +65,25 @@ class DepartmentController extends BaseController {
} }
public function update($DepartmentID = null) { public function update($DepartmentID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
try { if ($input === null) {
if (!$DepartmentID || !ctype_digit((string) $DepartmentID)) { return;
return $this->failValidationErrors('ID is required.');
} }
$input['DepartmentID'] = (int) $DepartmentID;
$id = $input['DepartmentID']; $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 {
$this->model->update($id, $input); $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) { } catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

@ -1,6 +1,7 @@
<?php <?php
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
@ -8,6 +9,7 @@ use App\Models\Organization\DisciplineModel;
class DisciplineController extends BaseController { class DisciplineController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -64,12 +66,24 @@ class DisciplineController extends BaseController {
} }
public function update($DisciplineID = null) { public function update($DisciplineID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if (!$DisciplineID || !ctype_digit((string) $DisciplineID)) { return $this->failValidationErrors('ID is required.'); } if ($input === null) {
$input['DisciplineID'] = (int) $DisciplineID; return;
$id = $input['DisciplineID']; }
$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); $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 { try {
$id = $input['DisciplineID']; $id = $input['DisciplineID'];

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Models\Organization\HostAppModel; use App\Models\Organization\HostAppModel;
class HostAppController extends BaseController { class HostAppController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -83,21 +85,29 @@ class HostAppController extends BaseController {
} }
public function update($HostAppID = null) { public function update($HostAppID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
try { return;
if ($HostAppID === null || $HostAppID === '') {
return $this->failValidationErrors('HostAppID is required.');
} }
if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $HostAppID) { $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.'); return $this->failValidationErrors('HostAppID in URL does not match body.');
} }
$id = $HostAppID;
$input['HostAppID'] = $id; $input['HostAppID'] = $id;
try {
$this->model->update($id, $input); $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) { } catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

@ -2,12 +2,14 @@
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Models\Organization\HostComParaModel; use App\Models\Organization\HostComParaModel;
class HostComParaController extends BaseController { class HostComParaController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -83,22 +85,30 @@ class HostComParaController extends BaseController {
} }
public function update($HostAppID = null) { public function update($HostAppID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if ($input === null) {
try { return;
if ($HostAppID === null || $HostAppID === '') {
return $this->failValidationErrors('HostAppID is required.');
} }
if (isset($input['HostAppID']) && (string) $input['HostAppID'] !== (string) $HostAppID) { $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.'); return $this->failValidationErrors('HostAppID in URL does not match body.');
} }
$id = $HostAppID;
$input['HostAppID'] = $id; $input['HostAppID'] = $id;
try {
$this->model->update($id, $input); $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) { } catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

@ -1,6 +1,7 @@
<?php <?php
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
@ -8,6 +9,7 @@ use App\Models\Organization\SiteModel;
class SiteController extends BaseController { class SiteController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -76,12 +78,22 @@ class SiteController extends BaseController {
} }
public function update($SiteID = null) { 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.'); } $id = $this->requirePatchId($SiteID, 'SiteID');
$input['SiteID'] = (int) $SiteID; if ($id === null) {
return;
}
$id = $input['SiteID']; $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'])) { if (!empty($input['SiteCode'])) {
$validation = service('validation'); $validation = service('validation');
@ -96,7 +108,7 @@ class SiteController extends BaseController {
try { try {
$this->model->update($id, $input); $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) { } catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

@ -1,6 +1,7 @@
<?php <?php
namespace App\Controllers\Organization; namespace App\Controllers\Organization;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
@ -8,6 +9,7 @@ use App\Models\Organization\WorkstationModel;
class WorkstationController extends BaseController { class WorkstationController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -64,15 +66,25 @@ class WorkstationController extends BaseController {
} }
public function update($WorkstationID = null) { public function update($WorkstationID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
try { if ($input === null) {
if (!$WorkstationID || !ctype_digit((string) $WorkstationID)) { return;
return $this->failValidationErrors('ID is required.');
} }
$input['WorkstationID'] = (int) $WorkstationID;
$id = $input['WorkstationID']; $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 {
$this->model->update($id, $input); $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) { } catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

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

View File

@ -2,12 +2,14 @@
namespace App\Controllers; namespace App\Controllers;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use CodeIgniter\Controller; use CodeIgniter\Controller;
use App\Models\PatResultModel; use App\Models\PatResultModel;
class ResultController extends Controller { class ResultController extends Controller {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $model; protected $model;
@ -39,6 +41,10 @@ class ResultController extends Controller {
->paginate($perPage, 'default', $page); ->paginate($perPage, 'default', $page);
} }
$results = is_array($results)
? array_map([$this, 'hydrateResultPayload'], $results)
: $results;
return $this->respond([ return $this->respond([
'status' => 'success', 'status' => 'success',
'message' => 'Results retrieved successfully', 'message' => 'Results retrieved successfully',
@ -71,6 +77,8 @@ class ResultController extends Controller {
], 404); ], 404);
} }
$result = $this->hydrateResultPayload($result);
return $this->respond([ return $this->respond([
'status' => 'success', 'status' => 'success',
'message' => 'Result retrieved successfully', 'message' => 'Result retrieved successfully',
@ -88,14 +96,12 @@ class ResultController extends Controller {
} }
/** /**
* Update result with validation * Create a new result entry
* PATCH /api/result/{id} * POST /api/result
*/ */
public function update($id) { public function create() {
try { $payload = $this->request->getJSON(true);
$data = $this->request->getJSON(true); if (!is_array($payload) || empty($payload)) {
if (empty($data)) {
return $this->respond([ return $this->respond([
'status' => 'failed', 'status' => 'failed',
'message' => 'No data provided', 'message' => 'No data provided',
@ -103,7 +109,66 @@ class ResultController extends Controller {
], 400); ], 400);
} }
$result = $this->model->updateWithValidation((int)$id, $data); 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->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']) { if (!$result['success']) {
return $this->respond([ return $this->respond([
@ -114,13 +179,13 @@ class ResultController extends Controller {
} }
// Get updated result with relations // Get updated result with relations
$updatedResult = $this->model->getWithRelations((int)$id); $updatedResult = $this->model->getWithRelations($validatedId);
return $this->respond([ return $this->respond([
'status' => 'success', 'status' => 'success',
'message' => $result['message'], 'message' => $result['message'],
'data' => [ 'data' => [
'result' => $updatedResult, 'result' => $updatedResult ? $this->hydrateResultPayload($updatedResult) : [],
'flag' => $result['flag'] 'flag' => $result['flag']
] ]
], 200); ], 200);
@ -176,4 +241,11 @@ class ResultController extends Controller {
], 500); ], 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\Rule\RuleDefModel;
use App\Models\Test\TestDefSiteModel; use App\Models\Test\TestDefSiteModel;
use App\Services\RuleExpressionService; use App\Services\RuleExpressionService;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
class RuleController extends BaseController class RuleController extends BaseController
{ {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected RuleDefModel $ruleDefModel; protected RuleDefModel $ruleDefModel;
@ -98,7 +100,10 @@ class RuleController extends BaseController
public function create() public function create()
{ {
$input = $this->request->getJSON(true) ?? []; $input = $this->requirePatchPayload($this->request->getJSON(true) ?? []);
if ($input === null) {
return;
}
$validation = service('validation'); $validation = service('validation');
$validation->setRules([ $validation->setRules([

View File

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

View File

@ -2,6 +2,7 @@
namespace App\Controllers\Specimen; namespace App\Controllers\Specimen;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Libraries\ValueSet; use App\Libraries\ValueSet;
@ -9,6 +10,7 @@ use App\Models\Specimen\SpecimenCollectionModel;
class SpecimenCollectionController extends BaseController { class SpecimenCollectionController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -67,15 +69,26 @@ class SpecimenCollectionController extends BaseController {
} }
public function update($SpcColID = null) { public function update($SpcColID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if (!$SpcColID || !ctype_digit((string) $SpcColID)) { if ($input === null) {
return $this->failValidationErrors('SpcColID is required.'); 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()); } if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
try { try {
$id = $this->model->update($input['SpcColID'], $input); $this->model->update($id, $input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]); return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) { } catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

@ -2,6 +2,7 @@
namespace App\Controllers\Specimen; namespace App\Controllers\Specimen;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Libraries\ValueSet; use App\Libraries\ValueSet;
@ -9,6 +10,7 @@ use App\Models\Specimen\SpecimenModel;
class SpecimenController extends BaseController { class SpecimenController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $model; protected $model;
@ -67,15 +69,26 @@ class SpecimenController extends BaseController {
} }
public function update($SID = null) { public function update($SID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if (!$SID || !ctype_digit((string) $SID)) { if ($input === null) {
return $this->failValidationErrors('SID is required.'); 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()); } if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); }
try { try {
$id = $this->model->update($input['SID'], $input); $this->model->update($id, $input);
return $this->respondCreated([ 'status' => 'success', 'message' => "data $id updated successfully" ]); return $this->respond([ 'status' => 'success', 'message' => 'data updated successfully', 'data' => $id ], 200);
} catch (\Exception $e) { } catch (\Exception $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->failServerError('Something went wrong: ' . $e->getMessage());
} }

View File

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

View File

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

View File

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

View File

@ -3,11 +3,13 @@
namespace App\Controllers\Test; namespace App\Controllers\Test;
use App\Controllers\BaseController; use App\Controllers\BaseController;
use App\Traits\PatchValidationTrait;
use App\Traits\ResponseTrait; use App\Traits\ResponseTrait;
use App\Models\Test\TestMapDetailModel; use App\Models\Test\TestMapDetailModel;
class TestMapDetailController extends BaseController { class TestMapDetailController extends BaseController {
use ResponseTrait; use ResponseTrait;
use PatchValidationTrait;
protected $db; protected $db;
protected $rules; protected $rules;
@ -99,19 +101,26 @@ class TestMapDetailController extends BaseController {
} }
public function update($TestMapDetailID = null) { public function update($TestMapDetailID = null) {
$input = $this->request->getJSON(true); $input = $this->requirePatchPayload($this->request->getJSON(true));
if (!$TestMapDetailID || !ctype_digit((string) $TestMapDetailID)) { if ($input === null) {
return $this->failValidationErrors('TestMapDetailID is required.'); 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) { if (isset($input['TestMapDetailID']) && (string) $input['TestMapDetailID'] !== (string) $id) {
return $this->failValidationErrors('TestMapDetailID in URL does not match body.'); return $this->failValidationErrors('TestMapDetailID in URL does not match body.');
} }
$input['TestMapDetailID'] = $id;
$input['TestMapDetailID'] = $id;
$validationInput = array_intersect_key($input, $this->patchRules); $validationInput = array_intersect_key($input, $this->patchRules);
if (!empty($validationInput) && !$this->validateData($validationInput, $this->patchRules)) { if (!empty($validationInput) && !$this->validateData($validationInput, $this->patchRules)) {
return $this->failValidationErrors($this->validator->getErrors()); return $this->failValidationErrors($this->validator->getErrors());
@ -121,7 +130,7 @@ class TestMapDetailController extends BaseController {
$this->model->update($id, $input); $this->model->update($id, $input);
return $this->respond([ return $this->respond([
'status' => 'success', 'status' => 'success',
'message' => "data updated successfully", 'message' => 'data updated successfully',
'data' => $id 'data' => $id
], 200); ], 200);
} catch (\Exception $e) { } catch (\Exception $e) {

View File

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

View File

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

View File

@ -1,13 +1,17 @@
<?php <?php
namespace App\Models\Location; namespace App\Models\Location;
use App\Models\BaseModel;
use App\Libraries\ValueSet; use App\Libraries\ValueSet;
use App\Models\BaseModel;
use App\Models\Location\LocationAddressModel;
class LocationModel extends BaseModel { class LocationModel extends BaseModel {
protected $table = 'location'; protected $table = 'location';
protected $primaryKey = 'LocationID'; protected $primaryKey = 'LocationID';
protected $allowedFields = ['SiteID', 'LocCode', 'Parent', 'LocFull', 'Description', 'LocType', 'CreateDate', 'EndDate']; 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 $useTimestamps = true;
protected $createdField = 'CreateDate'; protected $createdField = 'CreateDate';
protected $updatedField = ''; protected $updatedField = '';
@ -43,19 +47,59 @@ class LocationModel extends BaseModel {
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 { public function saveLocation(array $data): array {
$modelAddress = new \App\Models\Location\LocationAddressModel(); $modelAddress = new LocationAddressModel();
$db = \Config\Database::connect(); $db = \Config\Database::connect();
$db->transBegin(); $db->transBegin();
try { try {
if (!empty($data['LocationID'])) { if (!empty($data['LocationID'])) {
$LocationID = $data['LocationID']; $LocationID = $data['LocationID'];
$this->update($LocationID, $data); $locationPayload = $this->extractLocationFields($data);
$modelAddress->update($LocationID, $data); if ($locationPayload !== []) {
$this->update($LocationID, $locationPayload);
}
$this->persistAddressFields($LocationID, $data, $modelAddress);
} else { } else {
$LocationID = $this->insert($data, true); $LocationID = $this->insert($data, true);
$data['LocationID'] = $LocationID; $this->persistAddressFields($LocationID, $data, $modelAddress, true);
$modelAddress->insert($data);
} }
if ($db->transStatus() === false) { if ($db->transStatus() === false) {
$error = $db->error(); $error = $db->error();

View File

@ -17,7 +17,9 @@ class PatResultModel extends BaseModel {
'TestSiteID', 'TestSiteID',
'TestSiteCode', 'TestSiteCode',
'AspCnt', 'AspCnt',
'ResultCode',
'Result', 'Result',
'ResultValue',
'SampleType', 'SampleType',
'ResultDateTime', 'ResultDateTime',
'WorkstationID', 'WorkstationID',

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