Add Quiz feature: 10-question quizzes with progressive scoring (max 40 pts)
- Quizzes table with questions, answer options, attempts, answers - Question types: multiple_choice, exclusion, true_false, free_text - Progressive scoring: [1,1,2,2,3,3,4,6,8,10] = max 40 per quiz - Alpine.js countdown timer per question with auto-submit on timeout - Admin: CRUD for quizzes + per-question editor, JSON export/import - Child: quiz overview with best scores, question view, result breakdown - Nav: Quiz link in child header and admin sidebar
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
<?php
|
||||
namespace App\Http\Controllers\Admin;
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Models\{Quiz, QuizQuestion, QuizAnswerOption};
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class QuizQuestionController extends Controller {
|
||||
|
||||
public function create(Quiz $quiz) {
|
||||
return view('admin.quiz_questions.create', compact('quiz'));
|
||||
}
|
||||
|
||||
public function store(Request $r, Quiz $quiz) {
|
||||
$r->validate([
|
||||
'type' => 'required|in:multiple_choice,exclusion,true_false,free_text',
|
||||
'question_text' => 'required|string',
|
||||
'time_limit' => 'nullable|integer|min:5|max:120',
|
||||
'correct_answer' => 'nullable|string|max:200',
|
||||
'correct_option' => 'nullable|integer|min:0|max:3',
|
||||
'options' => 'array',
|
||||
'options.*.text' => 'nullable|string|max:200',
|
||||
]);
|
||||
$q = QuizQuestion::create([
|
||||
'quiz_id' => $quiz->id,
|
||||
'sort_order' => ($quiz->questions()->max('sort_order') ?? -1) + 1,
|
||||
'type' => $r->type,
|
||||
'question_text' => $r->question_text,
|
||||
'time_limit' => $r->time_limit ?: null,
|
||||
'correct_answer' => $r->correct_answer,
|
||||
]);
|
||||
if (in_array($r->type,['multiple_choice','exclusion'])) {
|
||||
$correctIdx = (int)$r->input('correct_option', 0);
|
||||
foreach (($r->options ?? []) as $i => $opt) {
|
||||
if (!empty($opt['text'])) {
|
||||
QuizAnswerOption::create([
|
||||
'quiz_question_id' => $q->id,
|
||||
'text' => $opt['text'],
|
||||
'is_correct' => $i === $correctIdx,
|
||||
'sort_order' => $i,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return redirect()->route('admin.quizzes.edit', $quiz)->with('success','Frage hinzugefügt.');
|
||||
}
|
||||
|
||||
public function edit(QuizQuestion $question) {
|
||||
return view('admin.quiz_questions.edit', compact('question'));
|
||||
}
|
||||
|
||||
public function update(Request $r, QuizQuestion $question) {
|
||||
$r->validate([
|
||||
'type' => 'required|in:multiple_choice,exclusion,true_false,free_text',
|
||||
'question_text' => 'required|string',
|
||||
'time_limit' => 'nullable|integer|min:5|max:120',
|
||||
'correct_answer' => 'nullable|string|max:200',
|
||||
'correct_option' => 'nullable|integer|min:0|max:3',
|
||||
'options' => 'array',
|
||||
'options.*.text' => 'nullable|string|max:200',
|
||||
]);
|
||||
$question->update([
|
||||
'type' => $r->type,
|
||||
'question_text' => $r->question_text,
|
||||
'time_limit' => $r->time_limit ?: null,
|
||||
'correct_answer' => $r->correct_answer,
|
||||
]);
|
||||
if (in_array($r->type,['multiple_choice','exclusion'])) {
|
||||
$question->answerOptions()->delete();
|
||||
$correctIdx = (int)$r->input('correct_option', 0);
|
||||
foreach (($r->options ?? []) as $i => $opt) {
|
||||
if (!empty($opt['text'])) {
|
||||
QuizAnswerOption::create([
|
||||
'quiz_question_id' => $question->id,
|
||||
'text' => $opt['text'],
|
||||
'is_correct' => $i === $correctIdx,
|
||||
'sort_order' => $i,
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return redirect()->route('admin.quizzes.edit', $question->quiz)->with('success','Frage gespeichert.');
|
||||
}
|
||||
|
||||
public function destroy(QuizQuestion $question) {
|
||||
$quiz = $question->quiz;
|
||||
$question->delete();
|
||||
return redirect()->route('admin.quizzes.edit', $quiz)->with('success','Frage gelöscht.');
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user