fix: handle contact details on create

Separate nested contact details from the base payload, propagate sync failures to the API response, and add a regression test covering contact creation with details.
This commit is contained in:
mahdahar 2026-04-13 13:16:06 +07:00
parent ee7b677ae4
commit 1c1808fdb9
3 changed files with 98 additions and 41 deletions

View File

@ -68,16 +68,25 @@ class ContactController extends BaseController {
} }
} }
public function create() { public function create() {
$input = $this->request->getJSON(true); $input = $this->request->getJSON(true);
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->saveContact($input,true); $result = $this->model->saveContact($input);
return $this->respondCreated([ 'status' => 'success', 'message' => 'data created successfully', 'data' => $id ], 201);
} catch (\Throwable $e) { if (($result['status'] ?? 'error') !== 'success') {
return $this->failServerError('Something went wrong: ' . $e->getMessage()); return $this->respond([
} 'status' => 'failed',
} 'message' => $result['message'] ?? 'Failed to create contact',
'data' => []
], 400);
}
return $this->respondCreated([ 'status' => 'success', 'message' => 'data created successfully', 'data' => $result ], 201);
} catch (\Throwable $e) {
return $this->failServerError('Something went wrong: ' . $e->getMessage());
}
}
public function update($ContactID = null) { public function update($ContactID = null) {
$input = $this->requirePatchPayload($this->request->getJSON(true)); $input = $this->requirePatchPayload($this->request->getJSON(true));

View File

@ -74,37 +74,41 @@ class ContactModel extends BaseModel {
return $contact; return $contact;
} }
public function saveContact(array $data): array { public function saveContact(array $data): array {
$db = \Config\Database::connect(); $db = \Config\Database::connect();
$db->transStart(); $db->transStart();
try { try {
if (!empty($data['ContactID'])) { $details = $data['Details'] ?? [];
$contactId = $data['ContactID']; unset($data['Details']);
$this->update($contactId, $data);
} else { if (!empty($data['ContactID'])) {
$contactId = $this->insert($data, true); $contactId = $data['ContactID'];
$this->update($contactId, $data);
} else {
$contactId = $this->insert($data, true);
} }
if (!$contactId) { if (!$contactId) {
throw new \RuntimeException('Failed to save contact'); throw new \RuntimeException('Failed to save contact');
} }
if (!empty($data['Details'])) { if (!empty($details)) {
$modelDetail = new \App\Models\Contact\ContactDetailModel(); $modelDetail = new \App\Models\Contact\ContactDetailModel();
$result = $modelDetail->syncDetails($contactId, $data['Details']); $result = $modelDetail->syncDetails($contactId, $details);
if ($result['status'] !== 'success') { if ($result['status'] !== 'success') {
throw new \RuntimeException('SyncDetails failed: ' . $result['message']); throw new \RuntimeException('SyncDetails failed: ' . $result['message']);
} }
} }
$db->transComplete(); $db->transComplete();
return [ return [
'status' => 'success', 'status' => 'success',
'ContactID' => $contactId, 'ContactID' => $contactId,
]; 'DetailsCount' => count($details),
];
} catch (\Throwable $e) { } catch (\Throwable $e) {
$db->transRollback(); $db->transRollback();

View File

@ -105,10 +105,54 @@ class ContactControllerTest extends CIUnitTestCase
$json = $result->getJSON(); $json = $result->getJSON();
$data = json_decode($json, true); $data = json_decode($json, true);
$this->assertEquals('success', $data['status']); $this->assertEquals('success', $data['status']);
$this->assertIsArray($data['data']); $this->assertIsArray($data['data']);
$this->assertEquals('success', $data['data']['status']); $this->assertEquals('success', $data['data']['status']);
$this->assertIsInt($data['data']['ContactID']); $this->assertIsInt($data['data']['ContactID']);
}
public function testCreateContactWithDetails()
{
$contactData = [
'NameFirst' => 'TestContact' . time(),
'NameLast' => 'LastName',
'Initial' => 'TC',
'Details' => [
[
'SiteID' => '1',
'ContactCode' => 'CODE1',
'ContactEmail' => 'code1@example.com',
'OccupationID' => '1',
'JobTitle' => 'Doctor',
'Department' => 'General',
],
[
'SiteID' => '2',
'ContactCode' => 'CODE2',
'ContactEmail' => 'code2@example.com',
'OccupationID' => '2',
'JobTitle' => 'Specialist',
'Department' => 'Laboratory',
],
],
];
$result = $this->withHeaders(['Cookie' => 'token=' . $this->token])
->withBody(json_encode($contactData))
->call('post', 'api/contact');
$result->assertStatus(201);
$data = json_decode($result->getJSON(), true);
$contactId = $data['data']['ContactID'] ?? null;
$this->assertIsInt($contactId);
$show = $this->callProtected('get', 'api/contact/' . $contactId);
$show->assertStatus(200);
$showData = json_decode($show->getJSON(), true)['data'];
$this->assertCount(2, $showData['Details']);
$this->assertEqualsCanonicalizing(['1', '2'], array_column($showData['Details'], 'SiteID'));
} }
public function testPartialUpdateContactWithSingleField() public function testPartialUpdateContactWithSingleField()
@ -121,7 +165,7 @@ class ContactControllerTest extends CIUnitTestCase
'NameFirst' => 'Patched' 'NameFirst' => 'Patched'
]); ]);
$patch->assertStatus(201); $patch->assertStatus(200);
$response = json_decode($patch->getJSON(), true); $response = json_decode($patch->getJSON(), true);
$this->assertEquals('success', $response['status']); $this->assertEquals('success', $response['status']);