enhanced questionnaire handling with stable key validation, improved note management, and added option key support

This commit is contained in:
2026-05-26 15:40:35 +02:00
parent 1b3d74d822
commit 7671c9f329
10 changed files with 876 additions and 100 deletions

View File

@ -19,7 +19,10 @@ case 'GET':
foreach ($questions as &$q) {
$q['isRequired'] = (int)$q['isRequired'];
$q['orderIndex'] = (int)$q['orderIndex'];
$q['configJson'] = $q['configJson'] ?: '{}';
$cfg = qdb_parse_config_json($q['configJson']);
$q['configJson'] = json_encode($cfg, JSON_UNESCAPED_UNICODE);
$q['questionKey'] = qdb_question_key($cfg, $q['defaultText']);
$q['localId'] = qdb_question_local_id($q['questionID'], $qnID);
$ao = $pdo->prepare("
SELECT answerOptionID, questionID, defaultText, points, orderIndex, nextQuestionId
FROM answer_option WHERE questionID = :qid ORDER BY orderIndex
@ -29,7 +32,10 @@ case 'GET':
foreach ($q['answerOptions'] as &$opt) {
$opt['points'] = (int)$opt['points'];
$opt['orderIndex'] = (int)$opt['orderIndex'];
$opt['optionKey'] = qdb_option_key($opt);
$opt['labelGerman'] = qdb_option_german_label($pdo, $opt['answerOptionID'], $opt['defaultText']);
}
unset($opt);
$tr = $pdo->prepare("SELECT languageCode, text FROM question_translation WHERE questionID = :qid");
$tr->execute([':qid' => $q['questionID']]);
$q['translations'] = $tr->fetchAll(PDO::FETCH_ASSOC);
@ -42,16 +48,24 @@ case 'GET':
case 'POST':
require_role(['admin', 'supervisor'], $tokenRec);
$body = read_json_body();
if (empty($body['questionnaireID']) || !isset($body['defaultText'])) {
json_error('BAD_REQUEST', 'questionnaireID and defaultText required', 400);
if (empty($body['questionnaireID']) || !isset($body['defaultText']) || empty($body['questionKey'])) {
json_error('BAD_REQUEST', 'questionnaireID, questionKey, and defaultText required', 400);
}
$qnID = $body['questionnaireID'];
$text = trim($body['defaultText']);
qdb_require_non_empty_german($text);
$questionKey = qdb_validate_stable_key((string)$body['questionKey'], 'Question key');
$type = trim($body['type'] ?? '');
$order = (int)($body['orderIndex'] ?? 0);
$req = (int)($body['isRequired'] ?? 0);
$config = $body['configJson'] ?? '{}';
$cfg = qdb_parse_config_json($body['configJson'] ?? '{}');
$config = qdb_normalize_question_config(
$cfg,
$questionKey,
$body['noteBefore'] ?? ($cfg['noteBefore'] ?? null),
$body['noteAfter'] ?? ($cfg['noteAfter'] ?? null)
);
$configJson = json_encode($config, JSON_UNESCAPED_UNICODE);
[$pdo, $tmpDb, $lockFp] = qdb_open(true);
try {
$chk = $pdo->prepare("SELECT 1 FROM questionnaire WHERE questionnaireID = :id");
@ -79,13 +93,15 @@ case 'POST':
$pdo->prepare("INSERT INTO question (questionID, questionnaireID, defaultText, type, orderIndex, isRequired, configJson)
VALUES (:id, :qid, :t, :ty, :o, :r, :cj)")
->execute([':id' => $id, ':qid' => $qnID, ':t' => $text, ':ty' => $type,
':o' => $order, ':r' => $req, ':cj' => $config]);
':o' => $order, ':r' => $req, ':cj' => $configJson]);
qdb_upsert_source_translation($pdo, 'question', $id, $text);
qdb_sync_question_note_strings($pdo, $questionKey, $config);
qdb_save($tmpDb, $lockFp);
json_success(['question' => [
'questionID' => $id, 'questionnaireID' => $qnID, 'defaultText' => $text,
'questionKey' => $questionKey, 'localId' => $localId,
'type' => $type, 'orderIndex' => $order, 'isRequired' => $req,
'configJson' => $config, 'answerOptions' => [], 'translations' => [],
'configJson' => $configJson, 'answerOptions' => [], 'translations' => [],
]]);
} catch (Throwable $e) {
qdb_discard($tmpDb, $lockFp);
@ -115,15 +131,31 @@ case 'PUT':
$type = trim($body['type'] ?? $row['type']);
$order = (int)($body['orderIndex'] ?? $row['orderIndex']);
$req = (int)($body['isRequired'] ?? $row['isRequired']);
$config = $body['configJson'] ?? $row['configJson'];
$oldCfg = qdb_parse_config_json($row['configJson']);
$questionKey = isset($body['questionKey'])
? qdb_validate_stable_key((string)$body['questionKey'], 'Question key')
: qdb_question_key($oldCfg, $row['defaultText']);
if ($questionKey === '') {
json_error('MISSING_FIELDS', 'questionKey is required', 400);
}
$cfgIn = isset($body['configJson']) ? qdb_parse_config_json($body['configJson']) : $oldCfg;
$config = qdb_normalize_question_config(
$cfgIn,
$questionKey,
$body['noteBefore'] ?? ($cfgIn['noteBefore'] ?? null),
$body['noteAfter'] ?? ($cfgIn['noteAfter'] ?? null)
);
$configJson = json_encode($config, JSON_UNESCAPED_UNICODE);
$pdo->prepare("UPDATE question SET defaultText = :t, type = :ty, orderIndex = :o, isRequired = :r, configJson = :cj WHERE questionID = :id")
->execute([':t' => $text, ':ty' => $type, ':o' => $order, ':r' => $req, ':cj' => $config, ':id' => $id]);
->execute([':t' => $text, ':ty' => $type, ':o' => $order, ':r' => $req, ':cj' => $configJson, ':id' => $id]);
qdb_upsert_source_translation($pdo, 'question', $id, $text);
qdb_sync_question_note_strings($pdo, $questionKey, $config);
qdb_save($tmpDb, $lockFp);
json_success(['question' => [
'questionID' => $id, 'questionnaireID' => $row['questionnaireID'],
'defaultText' => $text, 'type' => $type, 'orderIndex' => $order, 'isRequired' => $req,
'configJson' => $config,
'defaultText' => $text, 'questionKey' => $questionKey,
'type' => $type, 'orderIndex' => $order, 'isRequired' => $req,
'configJson' => $configJson,
]]);
} catch (Throwable $e) {
qdb_discard($tmpDb, $lockFp);