From 1c1808fdb9f7dd7d4a4205599d995e446affcc25 Mon Sep 17 00:00:00 2001 From: mahdahar <89adham@gmail.com> Date: Mon, 13 Apr 2026 13:16:06 +0700 Subject: [PATCH] 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. --- app/Controllers/Contact/ContactController.php | 29 ++++++---- app/Models/Contact/ContactModel.php | 56 ++++++++++--------- tests/feature/ContactControllerTest.php | 54 ++++++++++++++++-- 3 files changed, 98 insertions(+), 41 deletions(-) diff --git a/app/Controllers/Contact/ContactController.php b/app/Controllers/Contact/ContactController.php index 02e1c8e..be70628 100755 --- a/app/Controllers/Contact/ContactController.php +++ b/app/Controllers/Contact/ContactController.php @@ -68,16 +68,25 @@ class ContactController extends BaseController { } } - public function create() { - $input = $this->request->getJSON(true); - if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); } - try { - $id = $this->model->saveContact($input,true); - return $this->respondCreated([ 'status' => 'success', 'message' => 'data created successfully', 'data' => $id ], 201); - } catch (\Throwable $e) { - return $this->failServerError('Something went wrong: ' . $e->getMessage()); - } - } + public function create() { + $input = $this->request->getJSON(true); + if (!$this->validateData($input, $this->rules)) { return $this->failValidationErrors($this->validator->getErrors()); } + try { + $result = $this->model->saveContact($input); + + if (($result['status'] ?? 'error') !== 'success') { + 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) { $input = $this->requirePatchPayload($this->request->getJSON(true)); diff --git a/app/Models/Contact/ContactModel.php b/app/Models/Contact/ContactModel.php index 916bfed..5de4b2a 100755 --- a/app/Models/Contact/ContactModel.php +++ b/app/Models/Contact/ContactModel.php @@ -74,37 +74,41 @@ class ContactModel extends BaseModel { return $contact; } - public function saveContact(array $data): array { - $db = \Config\Database::connect(); - $db->transStart(); - - try { - if (!empty($data['ContactID'])) { - $contactId = $data['ContactID']; - $this->update($contactId, $data); - } else { - $contactId = $this->insert($data, true); + public function saveContact(array $data): array { + $db = \Config\Database::connect(); + $db->transStart(); + + try { + $details = $data['Details'] ?? []; + unset($data['Details']); + + if (!empty($data['ContactID'])) { + $contactId = $data['ContactID']; + $this->update($contactId, $data); + } else { + $contactId = $this->insert($data, true); } - if (!$contactId) { - throw new \RuntimeException('Failed to save contact'); - } - - if (!empty($data['Details'])) { - $modelDetail = new \App\Models\Contact\ContactDetailModel(); - $result = $modelDetail->syncDetails($contactId, $data['Details']); - - if ($result['status'] !== 'success') { - throw new \RuntimeException('SyncDetails failed: ' . $result['message']); - } - } + if (!$contactId) { + throw new \RuntimeException('Failed to save contact'); + } + + if (!empty($details)) { + $modelDetail = new \App\Models\Contact\ContactDetailModel(); + $result = $modelDetail->syncDetails($contactId, $details); + + if ($result['status'] !== 'success') { + throw new \RuntimeException('SyncDetails failed: ' . $result['message']); + } + } $db->transComplete(); - return [ - 'status' => 'success', - 'ContactID' => $contactId, - ]; + return [ + 'status' => 'success', + 'ContactID' => $contactId, + 'DetailsCount' => count($details), + ]; } catch (\Throwable $e) { $db->transRollback(); diff --git a/tests/feature/ContactControllerTest.php b/tests/feature/ContactControllerTest.php index 6671c36..bb4d61f 100755 --- a/tests/feature/ContactControllerTest.php +++ b/tests/feature/ContactControllerTest.php @@ -105,10 +105,54 @@ class ContactControllerTest extends CIUnitTestCase $json = $result->getJSON(); $data = json_decode($json, true); - $this->assertEquals('success', $data['status']); - $this->assertIsArray($data['data']); - $this->assertEquals('success', $data['data']['status']); - $this->assertIsInt($data['data']['ContactID']); + $this->assertEquals('success', $data['status']); + $this->assertIsArray($data['data']); + $this->assertEquals('success', $data['data']['status']); + $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() @@ -121,7 +165,7 @@ class ContactControllerTest extends CIUnitTestCase 'NameFirst' => 'Patched' ]); - $patch->assertStatus(201); + $patch->assertStatus(200); $response = json_decode($patch->getJSON(), true); $this->assertEquals('success', $response['status']);