isFinished) { return null; } $questions = $this->getOrderedQuestions($survey); $currentQuestionId = (int) $submission->currentQuestion; if ($currentQuestionId > 0) { foreach ($questions as $question) { if ((int) $question->id === $currentQuestionId) { return $question; } } } return $questions[0] ?? null; } public function determineNextQuestion(SurveyModel $survey, SurveyContentModel $currentQuestion, string $normalizedAnswer): ?SurveyContentModel { $questions = $this->getOrderedQuestions($survey); $configuredJumpTarget = $this->resolveYesNoJumpTarget($currentQuestion, $questions, $normalizedAnswer); if ($configuredJumpTarget instanceof SurveyContentModel) { return $configuredJumpTarget; } $conditionalTarget = $this->surveyEngine->resolveConditionalTarget($survey, $currentQuestion, $normalizedAnswer); if ($conditionalTarget instanceof SurveyContentModel) { return $conditionalTarget; } foreach ($questions as $index => $question) { if ((int) $question->id === (int) $currentQuestion->id) { return $questions[$index + 1] ?? null; } } return null; } public function getFirstQuestion(SurveyModel $survey): ?SurveyContentModel { $questions = $this->getOrderedQuestions($survey); return $questions[0] ?? null; } public function resolveQuestionById(SurveyModel $survey, int $questionId): ?SurveyContentModel { if ($questionId <= 0) { return null; } foreach ($this->getOrderedQuestions($survey) as $question) { if ((int) $question->id === $questionId) { return $question; } } return null; } /** * @return array{current:int,total:int,percentage:int} */ public function getProgress(SurveyModel $survey, ?SurveyContentModel $currentQuestion): array { $questions = $this->getOrderedQuestions($survey); $total = max(1, count($questions)); $current = 1; if ($currentQuestion instanceof SurveyContentModel) { foreach ($questions as $index => $question) { if ((int) $question->id === (int) $currentQuestion->id) { $current = $index + 1; break; } } } else { $current = $total; } return [ 'current' => $current, 'total' => $total, 'percentage' => (int) round(($current / $total) * 100), ]; } /** * Beschreibt das Sprungziel einer Ja/Nein-Antwort über Positionsnummer und Label. * * @return array{position:int,label:string}|null */ public function describeJumpTarget(SurveyModel $survey, int $targetQuestionId): ?array { if ($targetQuestionId <= 0) { return null; } foreach ($this->getOrderedQuestions($survey) as $index => $question) { if ((int) $question->id === $targetQuestionId) { return [ 'position' => $index + 1, 'label' => trim((string) $question->question), ]; } } return null; } /** * @return list */ private function getOrderedQuestions(SurveyModel $survey): array { $surveyId = (int) $survey->id; if ($this->surveyQuestionRepository->countPublishedBySurvey($surveyId) > 0) { return $this->surveyQuestionRepository->findAllBySurvey($surveyId, true); } return $this->surveyQuestionRepository->findAllBySurvey($surveyId); } /** * @param list $questions */ private function resolveYesNoJumpTarget(SurveyContentModel $currentQuestion, array $questions, string $normalizedAnswer): ?SurveyContentModel { if ('yes_no_maybe' !== (string) $currentQuestion->type) { return null; } $targetId = match ($normalizedAnswer) { 'yes' => (int) ($currentQuestion->jumpOnYes ?? 0), 'no' => (int) ($currentQuestion->jumpOnNo ?? 0), default => 0, }; if ($targetId <= 0) { return null; } foreach ($questions as $question) { if ((int) $question->id === $targetId) { return $question; } } return null; } }