175 lines
7.5 KiB
PHP
175 lines
7.5 KiB
PHP
<?php
|
|
namespace App\Http\Controllers\Admin;
|
|
use App\Http\Controllers\Controller;
|
|
use App\Models\Question;
|
|
use App\Models\Subject;
|
|
use App\Models\AnswerOption;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\Support\Facades\DB;
|
|
class QuestionController extends Controller {
|
|
public function index(Request $r) {
|
|
$subjects = Subject::withCount(['questions as total', 'activeQuestions as active'])->get();
|
|
$activeSubject = $subjects->firstWhere('id', $r->subject) ?? $subjects->first();
|
|
$questions = Question::where('subject_id', $activeSubject->id)
|
|
->latest()->paginate(25)->withQueryString();
|
|
return view('admin.questions.index', compact('questions','subjects','activeSubject'));
|
|
}
|
|
public function create(Request $r) {
|
|
$subjects = Subject::all();
|
|
$activeSubject = $subjects->firstWhere('id', $r->subject) ?? $subjects->first();
|
|
return view('admin.questions.create', compact('subjects','activeSubject'));
|
|
}
|
|
public function store(Request $r) {
|
|
$r->validate([
|
|
'subject_id' => 'required|exists:subjects,id',
|
|
'question_text' => 'required|string',
|
|
'type' => 'required|in:multiple_choice,number_input',
|
|
'difficulty' => 'required|in:1,2,3',
|
|
'options' => 'required_if:type,multiple_choice|array|min:2',
|
|
'options.*' => 'required|string',
|
|
'correct' => 'required_if:type,multiple_choice|integer',
|
|
'number_answer' => 'required_if:type,number_input',
|
|
]);
|
|
$pts = [1=>5, 2=>10, 3=>20][$r->difficulty];
|
|
$q = Question::create([
|
|
'subject_id' => $r->subject_id,
|
|
'question_text' => $r->question_text,
|
|
'type' => $r->type,
|
|
'difficulty' => $r->difficulty,
|
|
'points_value' => $pts,
|
|
'active' => true,
|
|
]);
|
|
if ($r->type === 'multiple_choice') {
|
|
foreach ($r->options as $i => $text) {
|
|
if (trim($text) === '') continue;
|
|
AnswerOption::create([
|
|
'question_id' => $q->id,
|
|
'text' => $text,
|
|
'is_correct' => ($i == $r->correct),
|
|
'sort_order' => $i,
|
|
]);
|
|
}
|
|
} else {
|
|
AnswerOption::create([
|
|
'question_id' => $q->id,
|
|
'text' => $r->number_answer,
|
|
'is_correct' => true,
|
|
'sort_order' => 0,
|
|
]);
|
|
}
|
|
return redirect()->route('admin.questions.index')->with('success','Frage gespeichert.');
|
|
}
|
|
public function edit(Question $question) {
|
|
$subjects = Subject::all();
|
|
$question->load('answerOptions');
|
|
return view('admin.questions.edit', compact('question','subjects'));
|
|
}
|
|
public function update(Request $r, Question $question) {
|
|
$r->validate([
|
|
'subject_id' => 'required|exists:subjects,id',
|
|
'question_text' => 'required|string',
|
|
'difficulty' => 'required|in:1,2,3',
|
|
'options' => 'array',
|
|
'options.*' => 'string',
|
|
'correct' => 'integer',
|
|
]);
|
|
$pts = [1=>5, 2=>10, 3=>20][$r->difficulty];
|
|
$question->update([
|
|
'subject_id' => $r->subject_id,
|
|
'question_text' => $r->question_text,
|
|
'difficulty' => $r->difficulty,
|
|
'points_value' => $pts,
|
|
'active' => $r->boolean('active', true),
|
|
]);
|
|
if ($r->filled('options')) {
|
|
$question->answerOptions()->delete();
|
|
foreach ($r->options as $i => $text) {
|
|
if (trim($text) === '') continue;
|
|
AnswerOption::create([
|
|
'question_id' => $question->id,
|
|
'text' => $text,
|
|
'is_correct' => ($i == $r->correct),
|
|
'sort_order' => $i,
|
|
]);
|
|
}
|
|
}
|
|
return redirect()->route('admin.questions.index')->with('success','Gespeichert.');
|
|
}
|
|
public function destroy(Question $question) {
|
|
$question->delete();
|
|
return redirect()->route('admin.questions.index')->with('success','Frage gelöscht.');
|
|
}
|
|
|
|
public function export(Request $r) {
|
|
$query = Question::with(['subject','answerOptions'])->latest();
|
|
if ($r->filled('subject')) $query->where('subject_id', $r->subject);
|
|
$data = $query->get()->map(fn($q) => [
|
|
'subject' => $q->subject->slug,
|
|
'question_text' => $q->question_text,
|
|
'type' => $q->type,
|
|
'difficulty' => $q->difficulty,
|
|
'points_value' => $q->points_value,
|
|
'active' => (bool)$q->active,
|
|
'options' => $q->answerOptions->map(fn($o) => [
|
|
'text' => $o->text,
|
|
'is_correct' => (bool)$o->is_correct,
|
|
])->values(),
|
|
]);
|
|
$filename = 'fragen-' . now()->format('Y-m-d') . '.json';
|
|
return response()->streamDownload(
|
|
fn() => print(json_encode($data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE)),
|
|
$filename,
|
|
['Content-Type' => 'application/json']
|
|
);
|
|
}
|
|
|
|
public function import(Request $r) {
|
|
$r->validate(['file' => 'required|file|mimes:json|max:2048']);
|
|
$raw = json_decode(file_get_contents($r->file('file')->getRealPath()), true);
|
|
if (!is_array($raw)) {
|
|
return back()->with('error', 'Ungültiges JSON-Format.');
|
|
}
|
|
$subjects = Subject::pluck('id','slug');
|
|
|
|
// Collect subject IDs that appear in the file
|
|
$affectedIds = collect($raw)
|
|
->pluck('subject')
|
|
->unique()
|
|
->filter(fn($s) => isset($subjects[$s]))
|
|
->map(fn($s) => $subjects[$s])
|
|
->values();
|
|
|
|
$imported = 0;
|
|
$skipped = 0;
|
|
DB::transaction(function() use ($raw, $subjects, $affectedIds, &$imported, &$skipped) {
|
|
// Delete existing questions for those subjects
|
|
Question::whereIn('subject_id', $affectedIds)->delete();
|
|
|
|
foreach ($raw as $item) {
|
|
if (empty($item['subject']) || !isset($subjects[$item['subject']])) { $skipped++; continue; }
|
|
if (empty($item['question_text'])) { $skipped++; continue; }
|
|
$q = Question::create([
|
|
'subject_id' => $subjects[$item['subject']],
|
|
'question_text' => $item['question_text'],
|
|
'type' => $item['type'] ?? 'multiple_choice',
|
|
'difficulty' => $item['difficulty'] ?? 1,
|
|
'points_value' => $item['points_value'] ?? 5,
|
|
'active' => $item['active'] ?? true,
|
|
]);
|
|
foreach (($item['options'] ?? []) as $i => $opt) {
|
|
AnswerOption::create([
|
|
'question_id' => $q->id,
|
|
'text' => $opt['text'],
|
|
'is_correct' => $opt['is_correct'] ?? false,
|
|
'sort_order' => $i,
|
|
]);
|
|
}
|
|
$imported++;
|
|
}
|
|
});
|
|
$msg = "{$imported} Fragen importiert (bestehende Fragen der betroffenen Fächer wurden ersetzt).";
|
|
if ($skipped) $msg .= " {$skipped} übersprungen (unbekanntes Fach oder fehlender Text).";
|
|
return back()->with('success', $msg);
|
|
}
|
|
}
|