Refine survey editor flow and auth styling

This commit is contained in:
Jürgen Mummert
2026-05-25 16:02:47 +02:00
parent e204da60b6
commit a893078945
18 changed files with 1649 additions and 313 deletions
+55 -36
View File
@@ -59,32 +59,34 @@ final class SurveyQuestionRepository
public function create(int $surveyId, array $data): SurveyContentModel
{
$sorting = (int) $this->connection->fetchOne('SELECT COALESCE(MAX(sorting), 0) FROM tl_survey_content WHERE pid = ?', [$surveyId]) + 128;
$type = (string) ($data['type'] ?? 'yes_no_maybe');
$isRangeType = 'range' === $type;
$this->connection->insert('tl_survey_content', [
'pid' => $surveyId,
'sorting' => $sorting,
'type' => (string) ($data['type'] ?? 'yes_no_maybe'),
'type' => $type,
'question' => (string) ($data['question'] ?? ''),
'description' => (string) ($data['description'] ?? ''),
'mandatory' => !empty($data['mandatory']) ? '1' : '',
'published' => !empty($data['published']) ? '1' : '',
'allowMaybe' => 'yes_no_maybe' === (string) ($data['type'] ?? 'yes_no_maybe') && !empty($data['allowMaybe']) ? '1' : '',
'jumpOnYes' => 'yes_no_maybe' === (string) ($data['type'] ?? 'yes_no_maybe') ? max(0, (int) ($data['jumpOnYes'] ?? 0)) : 0,
'jumpOnNo' => 'yes_no_maybe' === (string) ($data['type'] ?? 'yes_no_maybe') ? max(0, (int) ($data['jumpOnNo'] ?? 0)) : 0,
'allowMultiple' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') && !empty($data['allowMultiple']) ? '1' : '',
'answerOption1' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption1'] ?? '')) : '',
'answerOption2' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption2'] ?? '')) : '',
'answerOption3' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption3'] ?? '')) : '',
'answerOption4' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption4'] ?? '')) : '',
'answerOption5' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption5'] ?? '')) : '',
'answerOption6' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption6'] ?? '')) : '',
'answerOption7' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption7'] ?? '')) : '',
'answerOption8' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption8'] ?? '')) : '',
'answerOption9' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption9'] ?? '')) : '',
'answerOption10' => 'choice' === (string) ($data['type'] ?? 'yes_no_maybe') ? trim((string) ($data['answerOption10'] ?? '')) : '',
'rangeMin' => (int) ($data['rangeMin'] ?? 0),
'rangeMax' => (int) ($data['rangeMax'] ?? 10),
'rangeStep' => max(1, (int) ($data['rangeStep'] ?? 1)),
'allowMaybe' => 'yes_no_maybe' === $type && !empty($data['allowMaybe']) ? '1' : '',
'jumpOnYes' => 'yes_no_maybe' === $type ? max(0, (int) ($data['jumpOnYes'] ?? 0)) : 0,
'jumpOnNo' => 'yes_no_maybe' === $type ? max(0, (int) ($data['jumpOnNo'] ?? 0)) : 0,
'allowMultiple' => 'choice' === $type && !empty($data['allowMultiple']) ? '1' : '',
'answerOption1' => 'choice' === $type ? trim((string) ($data['answerOption1'] ?? '')) : '',
'answerOption2' => 'choice' === $type ? trim((string) ($data['answerOption2'] ?? '')) : '',
'answerOption3' => 'choice' === $type ? trim((string) ($data['answerOption3'] ?? '')) : '',
'answerOption4' => 'choice' === $type ? trim((string) ($data['answerOption4'] ?? '')) : '',
'answerOption5' => 'choice' === $type ? trim((string) ($data['answerOption5'] ?? '')) : '',
'answerOption6' => 'choice' === $type ? trim((string) ($data['answerOption6'] ?? '')) : '',
'answerOption7' => 'choice' === $type ? trim((string) ($data['answerOption7'] ?? '')) : '',
'answerOption8' => 'choice' === $type ? trim((string) ($data['answerOption8'] ?? '')) : '',
'answerOption9' => 'choice' === $type ? trim((string) ($data['answerOption9'] ?? '')) : '',
'answerOption10' => 'choice' === $type ? trim((string) ($data['answerOption10'] ?? '')) : '',
'rangeMin' => $isRangeType ? (int) ($data['rangeMin'] ?? 0) : 0,
'rangeMax' => $isRangeType ? (int) ($data['rangeMax'] ?? 10) : 10,
'rangeStep' => $isRangeType ? max(1, (int) ($data['rangeStep'] ?? 1)) : 1,
]);
$id = (int) $this->connection->lastInsertId();
@@ -94,29 +96,32 @@ final class SurveyQuestionRepository
public function update(SurveyContentModel $question, array $data): void
{
$type = (string) ($data['type'] ?? $question->type);
$isRangeType = 'range' === $type;
$this->connection->update('tl_survey_content', [
'type' => (string) ($data['type'] ?? $question->type),
'type' => $type,
'question' => (string) ($data['question'] ?? $question->question),
'description' => (string) ($data['description'] ?? $question->description),
'mandatory' => !empty($data['mandatory']) ? '1' : '',
'published' => !empty($data['published']) ? '1' : '',
'allowMaybe' => 'yes_no_maybe' === (string) ($data['type'] ?? $question->type) && !empty($data['allowMaybe']) ? '1' : '',
'jumpOnYes' => 'yes_no_maybe' === (string) ($data['type'] ?? $question->type) ? max(0, (int) ($data['jumpOnYes'] ?? $question->jumpOnYes)) : 0,
'jumpOnNo' => 'yes_no_maybe' === (string) ($data['type'] ?? $question->type) ? max(0, (int) ($data['jumpOnNo'] ?? $question->jumpOnNo)) : 0,
'allowMultiple' => 'choice' === (string) ($data['type'] ?? $question->type) && !empty($data['allowMultiple']) ? '1' : '',
'answerOption1' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption1'] ?? $question->answerOption1)) : '',
'answerOption2' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption2'] ?? $question->answerOption2)) : '',
'answerOption3' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption3'] ?? $question->answerOption3)) : '',
'answerOption4' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption4'] ?? $question->answerOption4)) : '',
'answerOption5' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption5'] ?? $question->answerOption5)) : '',
'answerOption6' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption6'] ?? $question->answerOption6)) : '',
'answerOption7' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption7'] ?? $question->answerOption7)) : '',
'answerOption8' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption8'] ?? $question->answerOption8)) : '',
'answerOption9' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption9'] ?? $question->answerOption9)) : '',
'answerOption10' => 'choice' === (string) ($data['type'] ?? $question->type) ? trim((string) ($data['answerOption10'] ?? $question->answerOption10)) : '',
'rangeMin' => (int) ($data['rangeMin'] ?? $question->rangeMin),
'rangeMax' => (int) ($data['rangeMax'] ?? $question->rangeMax),
'rangeStep' => max(1, (int) ($data['rangeStep'] ?? $question->rangeStep)),
'allowMaybe' => 'yes_no_maybe' === $type && !empty($data['allowMaybe']) ? '1' : '',
'jumpOnYes' => 'yes_no_maybe' === $type ? max(0, (int) ($data['jumpOnYes'] ?? $question->jumpOnYes)) : 0,
'jumpOnNo' => 'yes_no_maybe' === $type ? max(0, (int) ($data['jumpOnNo'] ?? $question->jumpOnNo)) : 0,
'allowMultiple' => 'choice' === $type && !empty($data['allowMultiple']) ? '1' : '',
'answerOption1' => 'choice' === $type ? trim((string) ($data['answerOption1'] ?? $question->answerOption1)) : '',
'answerOption2' => 'choice' === $type ? trim((string) ($data['answerOption2'] ?? $question->answerOption2)) : '',
'answerOption3' => 'choice' === $type ? trim((string) ($data['answerOption3'] ?? $question->answerOption3)) : '',
'answerOption4' => 'choice' === $type ? trim((string) ($data['answerOption4'] ?? $question->answerOption4)) : '',
'answerOption5' => 'choice' === $type ? trim((string) ($data['answerOption5'] ?? $question->answerOption5)) : '',
'answerOption6' => 'choice' === $type ? trim((string) ($data['answerOption6'] ?? $question->answerOption6)) : '',
'answerOption7' => 'choice' === $type ? trim((string) ($data['answerOption7'] ?? $question->answerOption7)) : '',
'answerOption8' => 'choice' === $type ? trim((string) ($data['answerOption8'] ?? $question->answerOption8)) : '',
'answerOption9' => 'choice' === $type ? trim((string) ($data['answerOption9'] ?? $question->answerOption9)) : '',
'answerOption10' => 'choice' === $type ? trim((string) ($data['answerOption10'] ?? $question->answerOption10)) : '',
'rangeMin' => $isRangeType ? (int) ($data['rangeMin'] ?? $question->rangeMin) : 0,
'rangeMax' => $isRangeType ? (int) ($data['rangeMax'] ?? $question->rangeMax) : 10,
'rangeStep' => $isRangeType ? max(1, (int) ($data['rangeStep'] ?? $question->rangeStep)) : 1,
], [
'id' => (int) $question->id,
]);
@@ -129,6 +134,20 @@ final class SurveyQuestionRepository
'pid' => $surveyId,
]);
$this->connection->update('tl_survey_content', [
'jumpOnYes' => 0,
], [
'pid' => $surveyId,
'jumpOnYes' => $questionId,
]);
$this->connection->update('tl_survey_content', [
'jumpOnNo' => 0,
], [
'pid' => $surveyId,
'jumpOnNo' => $questionId,
]);
$this->connection->delete('tl_survey_condition', [
'sourceQuestion' => $questionId,
]);
+28 -3
View File
@@ -6,6 +6,7 @@ namespace Mummert\SurveyBundle\Repository;
use Contao\CoreBundle\Framework\ContaoFramework;
use Contao\StringUtil;
use Doctrine\DBAL\ArrayParameterType;
use Doctrine\DBAL\Connection;
use Mummert\SurveyBundle\Model\SurveyModel;
@@ -67,12 +68,11 @@ final class SurveyRepository
s.isLocked,
s.updatedAt,
COUNT(DISTINCT q.id) AS questionCount,
COUNT(DISTINCT a.id) AS answerCount
COUNT(DISTINCT CASE WHEN sub.isFinished = 1 THEN sub.id END) AS participationCount
FROM tl_survey s
INNER JOIN tl_survey_editor e ON COALESCE(NULLIF(e.survey, 0), e.pid) = s.id AND e.member = :member
LEFT JOIN tl_survey_content q ON q.pid = s.id
LEFT JOIN tl_survey_submission sub ON sub.survey = s.id
LEFT JOIN tl_survey_answer a ON a.submission = sub.id
GROUP BY s.id
ORDER BY s.updatedAt DESC, s.title ASC
SQL,
@@ -91,7 +91,7 @@ final class SurveyRepository
'alias' => $this->ensurePublicAlias(null),
'category' => serialize($this->normalizeCategoryIds($data['category'] ?? [])),
'description' => (string) ($data['description'] ?? ''),
'published' => !empty($data['published']) ? '1' : '',
'published' => '',
'isLocked' => '',
'createdBy' => $memberId,
'updatedBy' => $memberId,
@@ -212,6 +212,31 @@ final class SurveyRepository
]);
}
public function deleteCascade(int $surveyId): void
{
$this->connection->transactional(function () use ($surveyId): void {
$submissionIds = array_values(array_map('intval', $this->connection->fetchFirstColumn(
'SELECT id FROM tl_survey_submission WHERE survey = ?',
[$surveyId],
)));
if ([] !== $submissionIds) {
$this->connection->executeStatement(
'DELETE FROM tl_survey_answer WHERE submission IN (?)',
[$submissionIds],
[ArrayParameterType::INTEGER],
);
}
$this->connection->delete('tl_survey_submission', ['survey' => $surveyId]);
$this->connection->delete('tl_survey_condition', ['pid' => $surveyId]);
$this->connection->delete('tl_survey_content', ['pid' => $surveyId]);
$this->connection->delete('tl_survey_editor', ['survey' => $surveyId]);
$this->connection->delete('tl_survey_editor', ['pid' => $surveyId]);
$this->connection->delete('tl_survey', ['id' => $surveyId]);
});
}
private function generateRandomPublicAlias(): string
{
$characters = 'abcdefghijklmnopqrstuvwxyz0123456789';