refactored export functionality

This commit is contained in:
2026-05-22 14:17:02 +02:00
parent 52d4ef42dc
commit 2616238dc8
6 changed files with 206 additions and 157 deletions

View File

@ -0,0 +1,155 @@
<?php
/**
* Branching walk for questionnaire exports/results (matches app all-in-one logic).
* Only questions on the active path from current answers are "reachable".
*/
function qrb_load_path_questions(PDO $pdo, string $questionnaireId): array {
$stmt = $pdo->prepare("
SELECT questionID, defaultText, type, orderIndex, configJson
FROM question
WHERE questionnaireID = :qn
ORDER BY orderIndex
");
$stmt->execute([':qn' => $questionnaireId]);
$path = [];
foreach ($stmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
$parts = explode('__', $row['questionID']);
$shortId = end($parts);
$config = json_decode($row['configJson'] ?? '{}', true) ?: [];
$options = [];
$ao = $pdo->prepare("
SELECT defaultText, nextQuestionId
FROM answer_option
WHERE questionID = :qid
ORDER BY orderIndex
");
$ao->execute([':qid' => $row['questionID']]);
foreach ($ao->fetchAll(PDO::FETCH_ASSOC) as $opt) {
$options[] = [
'text' => $opt['defaultText'],
'nextQuestionId' => $opt['nextQuestionId'] ?? '',
];
}
$symptoms = [];
if (!empty($config['symptoms']) && is_array($config['symptoms'])) {
$symptoms = $config['symptoms'];
}
$path[] = [
'questionID' => $row['questionID'],
'shortId' => $shortId,
'type' => $row['type'] ?? '',
'storageKey' => $row['defaultText'] !== '' ? $row['defaultText'] : $shortId,
'options' => $options,
'symptoms' => $symptoms,
'isLastPage' => ($row['type'] ?? '') === 'last_page',
];
}
return $path;
}
/**
* @param array<string, array> $answerMap keyed by full questionID
* @return array<string, bool> reachable full questionIDs
*/
function qrb_reachable_question_ids(PDO $pdo, string $questionnaireId, array $answerMap, array $optionTextMap): array {
$path = qrb_load_path_questions($pdo, $questionnaireId);
$reachable = [];
$shortToIndex = [];
foreach ($path as $idx => $q) {
$shortToIndex[$q['shortId']] = $idx;
}
$i = 0;
$count = count($path);
while ($i < $count) {
$q = $path[$i];
if ($q['isLastPage']) {
$i++;
continue;
}
$reachable[$q['questionID']] = true;
$nextTarget = qrb_resolve_next_target($q, $answerMap, $optionTextMap);
if ($nextTarget !== null) {
foreach ($path as $pq) {
if ($pq['isLastPage'] && $pq['shortId'] === $nextTarget) {
break 2;
}
}
if (isset($shortToIndex[$nextTarget])) {
$i = $shortToIndex[$nextTarget];
} else {
$i++;
}
} elseif (qrb_has_branching_options($q) && !qrb_has_branch_answer($q, $answerMap, $optionTextMap)) {
break;
} else {
$i++;
}
}
return $reachable;
}
function qrb_resolve_next_target(array $q, array $answerMap, array $optionTextMap): ?string {
$answer = $answerMap[$q['questionID']] ?? null;
if ($answer === null) {
return null;
}
$selectedText = qrb_answer_display_value($answer, $optionTextMap);
if ($selectedText === null || $selectedText === '') {
return null;
}
foreach ($q['options'] as $opt) {
if ($opt['text'] === $selectedText && $opt['nextQuestionId'] !== '') {
return $opt['nextQuestionId'];
}
}
return null;
}
function qrb_has_branching_options(array $q): bool {
foreach ($q['options'] as $opt) {
if ($opt['nextQuestionId'] !== '') {
return true;
}
}
return false;
}
function qrb_has_branch_answer(array $q, array $answerMap, array $optionTextMap): bool {
$answer = $answerMap[$q['questionID']] ?? null;
if ($answer === null) {
return false;
}
$v = qrb_answer_display_value($answer, $optionTextMap);
return $v !== null && $v !== '';
}
function qrb_answer_display_value(array $answer, array $optionTextMap): ?string {
if (!empty($answer['answerOptionID']) && isset($optionTextMap[$answer['answerOptionID']])) {
return $optionTextMap[$answer['answerOptionID']];
}
if (isset($answer['freeTextValue']) && $answer['freeTextValue'] !== '') {
return (string)$answer['freeTextValue'];
}
if (isset($answer['numericValue']) && $answer['numericValue'] !== null && $answer['numericValue'] !== '') {
return (string)$answer['numericValue'];
}
return null;
}
function qrb_format_cell(array $answer, array $optionTextMap): string {
$v = qrb_answer_display_value($answer, $optionTextMap);
return $v ?? '';
}