fix(testmap): align detail patch operation keys with API docs

This commit is contained in:
mahdahar 2026-04-09 15:32:16 +07:00
parent 7e38622070
commit c743049ed1
4 changed files with 24 additions and 24 deletions

View File

@ -95,8 +95,8 @@ class TestMapController extends BaseController {
try { try {
$id = $this->model->insert($headerInput); $id = $this->model->insert($headerInput);
if ($detailsPayload !== null && !empty($detailsPayload['new'])) { if ($detailsPayload !== null && !empty($detailsPayload['created'])) {
if (!$this->insertDetailRows($id, $detailsPayload['new'])) { if (!$this->insertDetailRows($id, $detailsPayload['created'])) {
$this->db->transRollback(); $this->db->transRollback();
return; return;
} }
@ -235,35 +235,35 @@ class TestMapController extends BaseController {
} }
if ($this->isDetailOpsPayload($detailsPayload)) { if ($this->isDetailOpsPayload($detailsPayload)) {
$newItems = $this->normalizeDetailList($detailsPayload['new'] ?? [], 'details.new'); $createdItems = $this->normalizeDetailList($detailsPayload['created'] ?? [], 'details.created');
if ($newItems === null) { return null; } if ($createdItems === null) { return null; }
$editItems = $this->normalizeDetailList($detailsPayload['edit'] ?? [], 'details.edit'); $editedItems = $this->normalizeDetailList($detailsPayload['edited'] ?? [], 'details.edited');
if ($editItems === null) { return null; } if ($editedItems === null) { return null; }
$deletedIds = $this->normalizeDetailIds($detailsPayload['deleted'] ?? []); $deletedIds = $this->normalizeDetailIds($detailsPayload['deleted'] ?? []);
if ($deletedIds === null) { return null; } if ($deletedIds === null) { return null; }
return ['new' => $newItems, 'edit' => $editItems, 'deleted' => $deletedIds]; return ['created' => $createdItems, 'edited' => $editedItems, 'deleted' => $deletedIds];
} }
if ($this->isListPayload($detailsPayload)) { if ($this->isListPayload($detailsPayload)) {
$items = $this->normalizeDetailList($detailsPayload, 'details'); $items = $this->normalizeDetailList($detailsPayload, 'details');
if ($items === null) { return null; } if ($items === null) { return null; }
return ['new' => $items, 'edit' => [], 'deleted' => []]; return ['created' => $items, 'edited' => [], 'deleted' => []];
} }
if ($this->isAssocArray($detailsPayload)) { if ($this->isAssocArray($detailsPayload)) {
$items = $this->normalizeDetailList([$detailsPayload], 'details'); $items = $this->normalizeDetailList([$detailsPayload], 'details');
if ($items === null) { return null; } if ($items === null) { return null; }
return ['new' => $items, 'edit' => [], 'deleted' => []]; return ['created' => $items, 'edited' => [], 'deleted' => []];
} }
$this->failValidationErrors('details must be an array of objects or contain new/edit/deleted arrays.'); $this->failValidationErrors('details must be an array of objects or contain created/edited/deleted arrays.');
return null; return null;
} }
private function applyDetailOperations(int $testMapID, array $operations): bool private function applyDetailOperations(int $testMapID, array $operations): bool
{ {
if (!empty($operations['edit']) && !$this->updateDetails($testMapID, $operations['edit'])) { if (!empty($operations['edited']) && !$this->updateDetails($testMapID, $operations['edited'])) {
return false; return false;
} }
@ -271,7 +271,7 @@ class TestMapController extends BaseController {
return false; return false;
} }
if (!empty($operations['new']) && !$this->insertDetailRows($testMapID, $operations['new'])) { if (!empty($operations['created']) && !$this->insertDetailRows($testMapID, $operations['created'])) {
return false; return false;
} }
@ -287,7 +287,7 @@ class TestMapController extends BaseController {
$prepared = []; $prepared = [];
foreach ($items as $index => $item) { foreach ($items as $index => $item) {
if (!$this->validateData($item, $this->detailRules)) { if (!$this->validateData($item, $this->detailRules)) {
$this->failValidationErrors(['details.new' => $this->validator->getErrors()]); $this->failValidationErrors(['details.created' => $this->validator->getErrors()]);
return false; return false;
} }
$prepared[] = array_merge(['TestMapID' => $testMapID], $item); $prepared[] = array_merge(['TestMapID' => $testMapID], $item);
@ -302,12 +302,12 @@ class TestMapController extends BaseController {
foreach ($items as $index => $detail) { foreach ($items as $index => $detail) {
$detailID = $detail['TestMapDetailID'] ?? null; $detailID = $detail['TestMapDetailID'] ?? null;
if (!$detailID || !ctype_digit((string) $detailID)) { if (!$detailID || !ctype_digit((string) $detailID)) {
$this->failValidationErrors("details.edit[{$index}].TestMapDetailID is required and must be an integer."); $this->failValidationErrors("details.edited[{$index}].TestMapDetailID is required and must be an integer.");
return false; return false;
} }
if (array_key_exists('TestMapID', $detail) && (int) $detail['TestMapID'] !== $testMapID) { if (array_key_exists('TestMapID', $detail) && (int) $detail['TestMapID'] !== $testMapID) {
$this->failValidationErrors("details.edit[{$index}] must belong to TestMap {$testMapID}."); $this->failValidationErrors("details.edited[{$index}] must belong to TestMap {$testMapID}.");
return false; return false;
} }
@ -367,7 +367,7 @@ class TestMapController extends BaseController {
private function isDetailOpsPayload(array $payload): bool private function isDetailOpsPayload(array $payload): bool
{ {
return (bool) array_intersect(array_keys($payload), ['new', 'edit', 'deleted']); return (bool) array_intersect(array_keys($payload), ['created', 'edited', 'deleted']);
} }
private function isListPayload(array $payload): bool private function isListPayload(array $payload): bool

View File

@ -4216,11 +4216,11 @@ paths:
details: details:
description: | description: |
Detail payload supports either a flat array/object (treated as new rows) Detail payload supports either a flat array/object (treated as new rows)
or an operations object with `new`, `edit`, and `deleted` arrays. or an operations object with `created`, `edited`, and `deleted` arrays.
oneOf: oneOf:
- type: object - type: object
properties: properties:
new: created:
type: array type: array
description: New detail records to insert description: New detail records to insert
items: items:
@ -4236,7 +4236,7 @@ paths:
type: string type: string
ClientTestName: ClientTestName:
type: string type: string
edit: edited:
type: array type: array
description: Existing detail records to update description: Existing detail records to update
items: items:

View File

@ -191,11 +191,11 @@
details: details:
description: | description: |
Detail payload supports either a flat array/object (treated as new rows) Detail payload supports either a flat array/object (treated as new rows)
or an operations object with `new`, `edit`, and `deleted` arrays. or an operations object with `created`, `edited`, and `deleted` arrays.
oneOf: oneOf:
- type: object - type: object
properties: properties:
new: created:
type: array type: array
description: New detail records to insert description: New detail records to insert
items: items:
@ -211,7 +211,7 @@
type: string type: string
ClientTestName: ClientTestName:
type: string type: string
edit: edited:
type: array type: array
description: Existing detail records to update description: Existing detail records to update
items: items:

View File

@ -188,13 +188,13 @@ class TestMapPatchTest extends CIUnitTestCase
->call('patch', "{$this->endpoint}/{$testMap['TestMapID']}", [ ->call('patch', "{$this->endpoint}/{$testMap['TestMapID']}", [
'ClientType' => 'WST', 'ClientType' => 'WST',
'details' => [ 'details' => [
'edit' => [ 'edited' => [
[ [
'TestMapDetailID' => $editDetail['TestMapDetailID'], 'TestMapDetailID' => $editDetail['TestMapDetailID'],
'ClientTestName' => 'Hemoglobin Updated', 'ClientTestName' => 'Hemoglobin Updated',
], ],
], ],
'new' => [ 'created' => [
[ [
'HostTestCode' => 'MCV', 'HostTestCode' => 'MCV',
'HostTestName' => 'MCV', 'HostTestName' => 'MCV',