Allow update endpoints to validate only provided fields, avoid overwriting unchanged data, and preserve existing PatDiag when omitted from PatVisit PATCH payloads.
725 lines
24 KiB
PHP
725 lines
24 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Test;
|
|
|
|
use App\Controllers\BaseController;
|
|
use App\Libraries\TestValidationService;
|
|
use App\Libraries\ValueSet;
|
|
use App\Traits\ResponseTrait;
|
|
|
|
class TestsController extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
|
|
protected $model;
|
|
protected $modelCal;
|
|
protected $modelGrp;
|
|
protected $modelMap;
|
|
protected $modelMapDetail;
|
|
protected $modelRefNum;
|
|
protected $modelRefTxt;
|
|
protected $rules;
|
|
|
|
public function __construct()
|
|
{
|
|
$this->model = new \App\Models\Test\TestDefSiteModel;
|
|
$this->modelCal = new \App\Models\Test\TestDefCalModel;
|
|
$this->modelGrp = new \App\Models\Test\TestDefGrpModel;
|
|
$this->modelMap = new \App\Models\Test\TestMapModel;
|
|
$this->modelMapDetail = new \App\Models\Test\TestMapDetailModel;
|
|
$this->modelRefNum = new \App\Models\RefRange\RefNumModel;
|
|
$this->modelRefTxt = new \App\Models\RefRange\RefTxtModel;
|
|
|
|
$this->rules = [
|
|
'TestSiteCode' => 'required',
|
|
'TestSiteName' => 'required',
|
|
'TestType' => 'required',
|
|
];
|
|
}
|
|
|
|
public function index()
|
|
{
|
|
$search = $this->request->getGet('search');
|
|
|
|
$filters = [
|
|
'SiteID' => $this->request->getGet('SiteID'),
|
|
'TestType' => $this->request->getGet('TestType'),
|
|
'isVisibleScr' => $this->request->getGet('isVisibleScr'),
|
|
'isVisibleRpt' => $this->request->getGet('isVisibleRpt'),
|
|
'TestSiteName' => $this->request->getGet('TestSiteName'),
|
|
'TestSiteCode' => $this->request->getGet('TestSiteCode'),
|
|
'search' => $search,
|
|
];
|
|
|
|
$rows = $this->model->getTestsWithRelations($filters);
|
|
|
|
if (empty($rows)) {
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'No data.',
|
|
'data' => [],
|
|
], 200);
|
|
}
|
|
|
|
$rows = ValueSet::transformLabels($rows, [
|
|
'TestType' => 'test_type',
|
|
]);
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Data fetched successfully',
|
|
'data' => $rows,
|
|
], 200);
|
|
}
|
|
|
|
public function show($id = null)
|
|
{
|
|
if (!$id) {
|
|
return $this->failValidationErrors('TestSiteID is required');
|
|
}
|
|
|
|
$row = $this->model->getTestById($id);
|
|
|
|
if (!$row) {
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'No data.',
|
|
'data' => null,
|
|
], 200);
|
|
}
|
|
|
|
$typeCode = $row['TestType'] ?? '';
|
|
|
|
if ($typeCode === 'CALC') {
|
|
$row['testdefcal'] = $this->modelCal->getByTestSiteID($id);
|
|
$row['testdefgrp'] = [
|
|
'members' => $this->modelGrp->getGroupMembers($id),
|
|
];
|
|
} elseif ($typeCode === 'GROUP') {
|
|
$row['testdefgrp'] = [
|
|
'members' => $this->modelGrp->getGroupMembers($id),
|
|
];
|
|
} elseif ($typeCode !== 'TITLE') {
|
|
$refType = $row['RefType'] ?? '';
|
|
$resultType = $row['ResultType'] ?? '';
|
|
|
|
if (TestValidationService::usesRefNum($resultType, $refType)) {
|
|
$row['refnum'] = $this->modelRefNum->getFormattedByTestSiteID($id);
|
|
}
|
|
|
|
if (TestValidationService::usesRefTxt($resultType, $refType)) {
|
|
$row['reftxt'] = $this->modelRefTxt->getFormattedByTestSiteID($id);
|
|
}
|
|
}
|
|
|
|
// Keep /api/test payload focused on test definition fields.
|
|
unset($row['testmap']);
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Data fetched successfully',
|
|
'data' => $row,
|
|
], 200);
|
|
}
|
|
|
|
public function create()
|
|
{
|
|
$input = $this->request->getJSON(true);
|
|
|
|
if (!$this->validateData($input, $this->rules)) {
|
|
return $this->failValidationErrors($this->validator->getErrors());
|
|
}
|
|
|
|
$testType = $input['TestType'] ?? '';
|
|
$details = $input['details'] ?? $input;
|
|
$resultType = $details['ResultType'] ?? '';
|
|
$refType = $details['RefType'] ?? '';
|
|
|
|
if (TestValidationService::isCalc($testType)) {
|
|
$resultType = 'NMRIC';
|
|
$refType = $refType ?: 'RANGE';
|
|
} elseif (TestValidationService::isGroup($testType) || TestValidationService::isTitle($testType)) {
|
|
$resultType = 'NORES';
|
|
$refType = 'NOREF';
|
|
}
|
|
|
|
if ($resultType && $refType) {
|
|
$validation = TestValidationService::validate($testType, $resultType, $refType);
|
|
if (!$validation['valid']) {
|
|
return $this->failValidationErrors(['type_validation' => $validation['error']]);
|
|
}
|
|
}
|
|
|
|
$db = \Config\Database::connect();
|
|
$db->transStart();
|
|
|
|
try {
|
|
$testSiteData = [
|
|
'SiteID' => array_key_exists('SiteID', $input) ? $input['SiteID'] : null,
|
|
'TestSiteCode'=> $input['TestSiteCode'],
|
|
'TestSiteName'=> $input['TestSiteName'],
|
|
'TestType' => $input['TestType'],
|
|
'Description' => $input['Description'] ?? null,
|
|
'SeqScr' => array_key_exists('SeqScr', $input) ? $input['SeqScr'] : null,
|
|
'SeqRpt' => array_key_exists('SeqRpt', $input) ? $input['SeqRpt'] : null,
|
|
'IndentLeft' => $input['IndentLeft'] ?? 0,
|
|
'FontStyle' => $input['FontStyle'] ?? null,
|
|
'isVisibleScr' => $input['isVisibleScr'] ?? 1,
|
|
'isVisibleRpt' => $input['isVisibleRpt'] ?? 1,
|
|
'isCountStat' => $input['isCountStat'] ?? 1,
|
|
'isRequestable' => $input['isRequestable'] ?? 1,
|
|
'StartDate' => $input['StartDate'] ?? date('Y-m-d H:i:s'),
|
|
];
|
|
|
|
$id = $this->model->insert($testSiteData);
|
|
if (!$id) {
|
|
$dbError = $db->error();
|
|
log_message('error', 'Test insert failed: ' . json_encode($dbError, JSON_UNESCAPED_SLASHES));
|
|
$message = $dbError['message'] ?? 'Failed to insert main test definition';
|
|
throw new \Exception('Failed to insert main test definition: ' . $message);
|
|
}
|
|
|
|
$this->handleDetails($id, $input, 'insert');
|
|
|
|
$db->transComplete();
|
|
|
|
if ($db->transStatus() === false) {
|
|
$dbError = $db->error();
|
|
$lastQuery = $db->showLastQuery();
|
|
log_message('error', 'TestController transaction failed: ' . json_encode([
|
|
'error' => $dbError,
|
|
'last_query' => $lastQuery,
|
|
], JSON_UNESCAPED_SLASHES));
|
|
return $this->failServerError('Transaction failed');
|
|
}
|
|
|
|
return $this->respondCreated([
|
|
'status' => 'created',
|
|
'message' => 'Test created successfully',
|
|
'data' => ['TestSiteId' => $id],
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$db->transRollback();
|
|
|
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
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');
|
|
}
|
|
|
|
$testType = $input['TestType'] ?? $existing['TestType'] ?? '';
|
|
$details = $input['details'] ?? $input;
|
|
$resultType = $details['ResultType'] ?? $existing['ResultType'] ?? '';
|
|
$refType = $details['RefType'] ?? $existing['RefType'] ?? '';
|
|
|
|
if (TestValidationService::isCalc($testType)) {
|
|
$resultType = 'NMRIC';
|
|
$refType = $refType ?: 'RANGE';
|
|
} elseif (TestValidationService::isGroup($testType) || TestValidationService::isTitle($testType)) {
|
|
$resultType = 'NORES';
|
|
$refType = 'NOREF';
|
|
}
|
|
|
|
if ($resultType && $refType) {
|
|
$validation = TestValidationService::validate($testType, $resultType, $refType);
|
|
if (!$validation['valid']) {
|
|
return $this->failValidationErrors(['type_validation' => $validation['error']]);
|
|
}
|
|
}
|
|
|
|
$db = \Config\Database::connect();
|
|
$db->transStart();
|
|
|
|
try {
|
|
$testSiteData = [];
|
|
$allowedUpdateFields = [
|
|
'TestSiteCode',
|
|
'TestSiteName',
|
|
'TestType',
|
|
'Description',
|
|
'SeqScr',
|
|
'SeqRpt',
|
|
'IndentLeft',
|
|
'FontStyle',
|
|
'isVisibleScr',
|
|
'isVisibleRpt',
|
|
'isCountStat',
|
|
'isRequestable',
|
|
'StartDate',
|
|
];
|
|
|
|
foreach ($allowedUpdateFields as $field) {
|
|
if (array_key_exists($field, $input)) {
|
|
$testSiteData[$field] = $input[$field];
|
|
}
|
|
}
|
|
|
|
if (!empty($testSiteData)) {
|
|
$this->model->update($id, $testSiteData);
|
|
}
|
|
|
|
$this->handleDetails($id, $input, 'update');
|
|
|
|
$db->transComplete();
|
|
|
|
if ($db->transStatus() === false) {
|
|
return $this->failServerError('Transaction failed');
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Test updated successfully',
|
|
'data' => ['TestSiteId' => $id],
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$db->transRollback();
|
|
|
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
public function delete($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');
|
|
}
|
|
|
|
if (!empty($existing['EndDate'])) {
|
|
return $this->failValidationErrors('Test is already disabled');
|
|
}
|
|
|
|
$db = \Config\Database::connect();
|
|
$db->transStart();
|
|
|
|
try {
|
|
$now = date('Y-m-d H:i:s');
|
|
|
|
$this->model->update($id, ['EndDate' => $now]);
|
|
|
|
$testType = $existing['TestType'];
|
|
$typeCode = $testType;
|
|
|
|
if (TestValidationService::isCalc($typeCode)) {
|
|
$this->modelCal->disableByTestSiteID($id);
|
|
$this->modelGrp->disableByTestSiteID($id);
|
|
} elseif (TestValidationService::isGroup($typeCode)) {
|
|
$this->modelGrp->disableByTestSiteID($id);
|
|
} elseif (TestValidationService::isTechnicalTest($typeCode)) {
|
|
$this->modelRefNum->disableByTestSiteID($id);
|
|
$this->modelRefTxt->disableByTestSiteID($id);
|
|
}
|
|
|
|
// Disable testmap by test code
|
|
$testSiteCode = $existing['TestSiteCode'] ?? null;
|
|
if ($testSiteCode) {
|
|
$existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode);
|
|
foreach ($existingMaps as $existingMap) {
|
|
$this->modelMapDetail->disableByTestMapID($existingMap['TestMapID']);
|
|
$this->modelMap->update($existingMap['TestMapID'], ['EndDate' => $now]);
|
|
}
|
|
}
|
|
|
|
$db->transComplete();
|
|
|
|
if ($db->transStatus() === false) {
|
|
return $this->failServerError('Transaction failed');
|
|
}
|
|
|
|
return $this->respond([
|
|
'status' => 'success',
|
|
'message' => 'Test disabled successfully',
|
|
'data' => ['TestSiteId' => $id, 'EndDate' => $now],
|
|
]);
|
|
} catch (\Exception $e) {
|
|
$db->transRollback();
|
|
|
|
return $this->failServerError('Something went wrong: ' . $e->getMessage());
|
|
}
|
|
}
|
|
|
|
private function handleDetails($testSiteID, $input, $action)
|
|
{
|
|
$testTypeID = $input['TestType'] ?? null;
|
|
$testSiteCode = null;
|
|
|
|
if (!$testTypeID && $action === 'update') {
|
|
$existing = $this->model->find($testSiteID);
|
|
$testTypeID = $existing['TestType'] ?? null;
|
|
$testSiteCode = $existing['TestSiteCode'] ?? null;
|
|
}
|
|
|
|
if (!$testTypeID) {
|
|
return;
|
|
}
|
|
|
|
$typeCode = $testTypeID;
|
|
|
|
$details = $input['details'] ?? $input;
|
|
$details['TestSiteID'] = $testSiteID;
|
|
$details['SiteID'] = array_key_exists('SiteID', $input) ? $input['SiteID'] : null;
|
|
|
|
switch ($typeCode) {
|
|
case 'CALC':
|
|
$this->saveCalcDetails($testSiteID, $details, $input, $action);
|
|
|
|
break;
|
|
|
|
case 'GROUP':
|
|
$this->saveGroupDetails($testSiteID, $details, $input, $action);
|
|
|
|
break;
|
|
|
|
case 'TITLE':
|
|
break;
|
|
|
|
case 'TEST':
|
|
case 'PARAM':
|
|
default:
|
|
$this->saveTechDetails($testSiteID, $details, $action, $typeCode);
|
|
|
|
if (in_array($typeCode, ['TEST', 'PARAM']) && isset($details['RefType'])) {
|
|
$refType = (string) $details['RefType'];
|
|
$resultType = $details['ResultType'] ?? '';
|
|
|
|
if (TestValidationService::usesRefNum($resultType, $refType) && isset($input['refnum']) && is_array($input['refnum'])) {
|
|
$this->saveRefNumRanges($testSiteID, $input['refnum'], $action, array_key_exists('SiteID', $input) ? $input['SiteID'] : null);
|
|
}
|
|
|
|
if (TestValidationService::usesRefTxt($resultType, $refType) && isset($input['reftxt']) && is_array($input['reftxt'])) {
|
|
$this->saveRefTxtRanges($testSiteID, $input['reftxt'], $action, array_key_exists('SiteID', $input) ? $input['SiteID'] : null);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
private function saveTechDetails($testSiteID, $data, $action, $typeCode)
|
|
{
|
|
$allowedFields = [
|
|
'DisciplineID',
|
|
'DepartmentID',
|
|
'ResultType',
|
|
'RefType',
|
|
'VSet',
|
|
'ReqQty',
|
|
'ReqQtyUnit',
|
|
'Unit1',
|
|
'Factor',
|
|
'Unit2',
|
|
'Decimal',
|
|
'CollReq',
|
|
'Method',
|
|
'ExpectedTAT',
|
|
];
|
|
|
|
$techData = [];
|
|
foreach ($allowedFields as $field) {
|
|
if (array_key_exists($field, $data)) {
|
|
$techData[$field] = $data[$field];
|
|
}
|
|
}
|
|
|
|
if ($techData !== []) {
|
|
$this->model->update($testSiteID, $techData);
|
|
}
|
|
}
|
|
|
|
private function saveRefNumRanges($testSiteID, $ranges, $action, $siteID)
|
|
{
|
|
if ($action === 'update') {
|
|
$this->modelRefNum->disableByTestSiteID($testSiteID);
|
|
}
|
|
|
|
$this->modelRefNum->batchInsert($testSiteID, $siteID, $ranges);
|
|
}
|
|
|
|
private function saveRefTxtRanges($testSiteID, $ranges, $action, $siteID)
|
|
{
|
|
if ($action === 'update') {
|
|
$this->modelRefTxt->disableByTestSiteID($testSiteID);
|
|
}
|
|
|
|
$this->modelRefTxt->batchInsert($testSiteID, $siteID, $ranges);
|
|
}
|
|
|
|
private function saveCalcDetails($testSiteID, $data, $input, $action)
|
|
{
|
|
$calcData = [];
|
|
$fieldMap = [
|
|
'DisciplineID' => 'DisciplineID',
|
|
'DepartmentID' => 'DepartmentID',
|
|
'Factor' => 'Factor',
|
|
'Unit2' => 'Unit2',
|
|
'Decimal' => 'Decimal',
|
|
'Method' => 'Method',
|
|
];
|
|
|
|
foreach ($fieldMap as $source => $target) {
|
|
if (array_key_exists($source, $data)) {
|
|
$calcData[$target] = $data[$source];
|
|
}
|
|
}
|
|
|
|
if (array_key_exists('FormulaCode', $data) || array_key_exists('Formula', $data)) {
|
|
$calcData['FormulaCode'] = $data['FormulaCode'] ?? $data['Formula'] ?? null;
|
|
}
|
|
|
|
if (array_key_exists('RefType', $data)) {
|
|
$calcData['RefType'] = $data['RefType'];
|
|
}
|
|
|
|
if (array_key_exists('Unit1', $data) || array_key_exists('ResultUnit', $data)) {
|
|
$calcData['Unit1'] = $data['Unit1'] ?? $data['ResultUnit'] ?? null;
|
|
}
|
|
|
|
$hasMemberPayload = isset($input['testdefgrp'])
|
|
&& is_array($input['testdefgrp'])
|
|
&& array_key_exists('members', $input['testdefgrp']);
|
|
|
|
if ($action === 'insert' && !array_key_exists('ResultType', $calcData)) {
|
|
$calcData['ResultType'] = 'NMRIC';
|
|
}
|
|
|
|
if ($action === 'insert' && !array_key_exists('RefType', $calcData)) {
|
|
$calcData['RefType'] = 'RANGE';
|
|
}
|
|
|
|
if ($calcData !== []) {
|
|
$calcData['TestSiteID'] = $testSiteID;
|
|
if ($action === 'update') {
|
|
$exists = $this->modelCal->existsByTestSiteID($testSiteID);
|
|
|
|
if ($exists) {
|
|
unset($calcData['TestSiteID']);
|
|
$this->modelCal->update($exists['TestCalID'], $calcData);
|
|
} else {
|
|
if (!array_key_exists('ResultType', $calcData)) {
|
|
$calcData['ResultType'] = 'NMRIC';
|
|
}
|
|
if (!array_key_exists('RefType', $calcData)) {
|
|
$calcData['RefType'] = 'RANGE';
|
|
}
|
|
$this->modelCal->insert($calcData);
|
|
}
|
|
} else {
|
|
$this->modelCal->insert($calcData);
|
|
}
|
|
}
|
|
|
|
if ($action === 'update' && !$hasMemberPayload) {
|
|
return;
|
|
}
|
|
|
|
if ($action === 'update') {
|
|
$this->modelGrp->disableByTestSiteID($testSiteID);
|
|
}
|
|
|
|
$memberIDs = $this->resolveMemberIDs($input);
|
|
|
|
$validation = $this->validateMemberIDs($memberIDs);
|
|
if (!$validation['valid']) {
|
|
throw new \Exception('Invalid member TestSiteID(s): ' . implode(', ', $validation['invalid']) . '. Make sure to use TestSiteID, not SeqScr or other values.');
|
|
}
|
|
|
|
foreach ($memberIDs as $memberID) {
|
|
$this->modelGrp->insert([
|
|
'TestSiteID' => $testSiteID,
|
|
'Member' => $memberID,
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function resolveMemberIDs(array $input): array
|
|
{
|
|
$memberIDs = [];
|
|
|
|
$rawMembers = $input['testdefgrp']['members'] ?? [];
|
|
if (is_array($rawMembers)) {
|
|
foreach ($rawMembers as $member) {
|
|
if (is_array($member)) {
|
|
$rawID = $member['TestSiteID'] ?? null;
|
|
} else {
|
|
$rawID = is_numeric($member) ? $member : null;
|
|
}
|
|
|
|
if ($rawID !== null && is_numeric($rawID)) {
|
|
$memberIDs[] = (int) $rawID;
|
|
}
|
|
}
|
|
}
|
|
|
|
$memberIDs = array_values(array_unique(array_filter($memberIDs)));
|
|
|
|
return $memberIDs;
|
|
}
|
|
|
|
/**
|
|
* Validate that member IDs exist in testdefsite table
|
|
*
|
|
* @param array $memberIDs Array of TestSiteID values to validate
|
|
* @return array ['valid' => bool, 'invalid' => array]
|
|
*/
|
|
private function validateMemberIDs(array $memberIDs): array
|
|
{
|
|
if (empty($memberIDs)) {
|
|
return ['valid' => true, 'invalid' => []];
|
|
}
|
|
|
|
$existing = $this->model->whereIn('TestSiteID', $memberIDs)
|
|
->where('EndDate IS NULL')
|
|
->findAll();
|
|
|
|
$existingIDs = array_column($existing, 'TestSiteID');
|
|
$invalidIDs = array_diff($memberIDs, $existingIDs);
|
|
|
|
return [
|
|
'valid' => empty($invalidIDs),
|
|
'invalid' => array_values($invalidIDs)
|
|
];
|
|
}
|
|
|
|
private function saveGroupDetails($testSiteID, $data, $input, $action)
|
|
{
|
|
$hasMemberPayload = isset($input['testdefgrp'])
|
|
&& is_array($input['testdefgrp'])
|
|
&& array_key_exists('members', $input['testdefgrp']);
|
|
|
|
if ($action === 'update' && !$hasMemberPayload) {
|
|
return;
|
|
}
|
|
|
|
if ($action === 'update') {
|
|
$this->modelGrp->disableByTestSiteID($testSiteID);
|
|
}
|
|
|
|
$memberIDs = $this->resolveMemberIDs($input);
|
|
|
|
// Validate member IDs before insertion
|
|
$validation = $this->validateMemberIDs($memberIDs);
|
|
if (!$validation['valid']) {
|
|
throw new \Exception('Invalid member TestSiteID(s): ' . implode(', ', $validation['invalid']) . '. Make sure to use TestSiteID, not SeqScr or other values.');
|
|
}
|
|
|
|
foreach ($memberIDs as $memberID) {
|
|
$this->modelGrp->insert([
|
|
'TestSiteID' => $testSiteID,
|
|
'Member' => $memberID,
|
|
]);
|
|
}
|
|
}
|
|
|
|
private function saveTestMap($testSiteID, $testSiteCode, $mappings, $action)
|
|
{
|
|
if ($action === 'update' && $testSiteCode) {
|
|
// Find existing mappings by test code through testmapdetail
|
|
$existingMaps = $this->modelMap->getMappingsByTestCode($testSiteCode);
|
|
|
|
foreach ($existingMaps as $existingMap) {
|
|
$this->modelMapDetail->disableByTestMapID($existingMap['TestMapID']);
|
|
}
|
|
|
|
// Soft delete the testmap headers
|
|
foreach ($existingMaps as $existingMap) {
|
|
$this->modelMap->update($existingMap['TestMapID'], ['EndDate' => date('Y-m-d H:i:s')]);
|
|
}
|
|
}
|
|
|
|
foreach ($this->normalizeTestMapPayload($mappings) as $map) {
|
|
$mapData = [
|
|
'HostType' => $map['HostType'] ?? null,
|
|
'HostID' => $map['HostID'] ?? null,
|
|
'ClientType' => $map['ClientType'] ?? null,
|
|
'ClientID' => $map['ClientID'] ?? null,
|
|
];
|
|
|
|
$testMapID = $this->modelMap->insert($mapData);
|
|
if (!$testMapID) {
|
|
continue;
|
|
}
|
|
|
|
foreach ($this->extractTestMapDetails($map) as $detail) {
|
|
$detailData = [
|
|
'TestMapID' => $testMapID,
|
|
'HostTestCode' => $detail['HostTestCode'] ?? null,
|
|
'HostTestName' => $detail['HostTestName'] ?? null,
|
|
'ConDefID' => $detail['ConDefID'] ?? null,
|
|
'ClientTestCode' => $detail['ClientTestCode'] ?? null,
|
|
'ClientTestName' => $detail['ClientTestName'] ?? null,
|
|
];
|
|
$this->modelMapDetail->insert($detailData);
|
|
}
|
|
}
|
|
}
|
|
|
|
private function normalizeTestMapPayload($mappings): array
|
|
{
|
|
if (!is_array($mappings)) {
|
|
return [];
|
|
}
|
|
|
|
if ($this->isAssoc($mappings)) {
|
|
return [$mappings];
|
|
}
|
|
|
|
return array_values(array_filter($mappings, static fn ($map) => is_array($map)));
|
|
}
|
|
|
|
private function extractTestMapDetails(array $map): array
|
|
{
|
|
if (isset($map['details']) && is_array($map['details'])) {
|
|
return array_values(array_filter($map['details'], static fn ($detail) => is_array($detail)));
|
|
}
|
|
|
|
$flatDetail = [
|
|
'HostTestCode' => $map['HostTestCode'] ?? null,
|
|
'HostTestName' => $map['HostTestName'] ?? null,
|
|
'ConDefID' => $map['ConDefID'] ?? null,
|
|
'ClientTestCode' => $map['ClientTestCode'] ?? null,
|
|
'ClientTestName' => $map['ClientTestName'] ?? null,
|
|
];
|
|
|
|
foreach ($flatDetail as $value) {
|
|
if ($value !== null && $value !== '') {
|
|
return [$flatDetail];
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
private function isAssoc(array $array): bool
|
|
{
|
|
if ($array === []) {
|
|
return false;
|
|
}
|
|
|
|
return array_keys($array) !== range(0, count($array) - 1);
|
|
}
|
|
}
|