736 lines
25 KiB
PHP
736 lines
25 KiB
PHP
<?php
|
|
|
|
namespace App\Controllers\Test;
|
|
|
|
use App\Controllers\BaseController;
|
|
use App\Libraries\TestValidationService;
|
|
use App\Libraries\ValueSet;
|
|
use App\Traits\PatchValidationTrait;
|
|
use App\Traits\ResponseTrait;
|
|
|
|
class TestsController extends BaseController
|
|
{
|
|
use ResponseTrait;
|
|
use PatchValidationTrait;
|
|
|
|
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->requirePatchPayload($this->request->getJSON(true));
|
|
if ($input === null) {
|
|
return;
|
|
}
|
|
|
|
if (!$id && isset($input['TestSiteID'])) {
|
|
$id = $input['TestSiteID'];
|
|
}
|
|
|
|
$id = $this->requirePatchId($id, 'TestSiteID');
|
|
if ($id === null) {
|
|
return;
|
|
}
|
|
|
|
$existing = $this->model->find($id);
|
|
if (!$existing) {
|
|
return $this->respond([
|
|
'status' => 'failed',
|
|
'message' => 'Test not found',
|
|
'data' => []
|
|
], 404);
|
|
}
|
|
|
|
$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);
|
|
}
|
|
}
|