diff --git a/app/Http/Controllers/Api/ExerciseController.php b/app/Http/Controllers/Api/ExerciseController.php index 836e8fe..1d4a14a 100644 --- a/app/Http/Controllers/Api/ExerciseController.php +++ b/app/Http/Controllers/Api/ExerciseController.php @@ -75,4 +75,23 @@ class ExerciseController extends Controller ], 500); } } + + public function detail($id) + { + $exercise = $this->exerciseService->detail($id); + + if (empty($exercise)) { + return response()->json([ + 'status' => true, + 'data' => $exercise, + 'message' => 'Không có dữ liệu.', + ]); + } + + return response()->json([ + 'status' => true, + 'data' => $exercise, + 'message' => 'Lấy danh sách đề thi thành công.', + ]); + } } diff --git a/app/Http/Resources/BlankResource.php b/app/Http/Resources/BlankResource.php new file mode 100644 index 0000000..43becc4 --- /dev/null +++ b/app/Http/Resources/BlankResource.php @@ -0,0 +1,24 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'position' => $this->position, + 'correct_answer' => $this->correct_answer, + 'other_answers' => $this->other_answers, + ]; + } +} diff --git a/app/Http/Resources/ChoiceResource.php b/app/Http/Resources/ChoiceResource.php new file mode 100644 index 0000000..850db6d --- /dev/null +++ b/app/Http/Resources/ChoiceResource.php @@ -0,0 +1,24 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'content' => $this->content, + 'is_correct' => $this->is_correct, + 'position' => $this->position, + ]; + } +} diff --git a/app/Http/Resources/ExerciseResource.php b/app/Http/Resources/ExerciseResource.php new file mode 100644 index 0000000..494c69c --- /dev/null +++ b/app/Http/Resources/ExerciseResource.php @@ -0,0 +1,27 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->lesson_name, + 'description' => $this->description, + 'level' => $this->level_label, + 'year' => $this->year, + 'skills' => SkillResource::collection($this->whenLoaded('skills')), + 'question_groups' => QuestionGroupResource::collection($this->whenLoaded('questionGroups')), + ]; + } +} diff --git a/app/Http/Resources/QuestionGroupResource.php b/app/Http/Resources/QuestionGroupResource.php new file mode 100644 index 0000000..2731831 --- /dev/null +++ b/app/Http/Resources/QuestionGroupResource.php @@ -0,0 +1,23 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'content' => $this->content, + 'questions' => QuestionResource::collection($this->whenLoaded('questions')), + ]; + } +} diff --git a/app/Http/Resources/QuestionResource.php b/app/Http/Resources/QuestionResource.php new file mode 100644 index 0000000..c4979b7 --- /dev/null +++ b/app/Http/Resources/QuestionResource.php @@ -0,0 +1,25 @@ + $this->id, + 'content' => $this->content, + 'score' => $this->score, + 'explanation' => $this->explanation, + 'hint' => $this->hint, + 'type' => [ + 'code' => $this->type->code ?? null, + 'name' => $this->type->name ?? null, + ], + 'choices' => $this->when($this->type->code === 'multiple_choice', ChoiceResource::collection($this->choices)), + 'blanks' => $this->when($this->type->code !== 'multiple_choice', BlankResource::collection($this->blanks)), + ]; + } +} diff --git a/app/Http/Resources/SkillResource.php b/app/Http/Resources/SkillResource.php new file mode 100644 index 0000000..8001e20 --- /dev/null +++ b/app/Http/Resources/SkillResource.php @@ -0,0 +1,22 @@ + + */ + public function toArray(Request $request): array + { + return [ + 'id' => $this->id, + 'name' => $this->name + ]; + } +} diff --git a/app/Models/Exercise.php b/app/Models/Exercise.php index 56c9461..1cef5f0 100644 --- a/app/Models/Exercise.php +++ b/app/Models/Exercise.php @@ -23,6 +23,18 @@ class Exercise extends Model 'media_object_id', ]; + protected $appends = ['level_label']; + + public function getLevelLabelAttribute() + { + return match ($this->level) { + 0 => 'easy', + 1 => 'normal', + 2 => 'hard', + default => 'normal', + }; + } + public function subject() { return $this->belongsTo(Subject::class, 'subject_id'); diff --git a/app/Models/QuestionBlank.php b/app/Models/QuestionBlank.php index 235ad61..69fc07c 100644 --- a/app/Models/QuestionBlank.php +++ b/app/Models/QuestionBlank.php @@ -17,6 +17,10 @@ class QuestionBlank extends Model 'other_answers', ]; + protected $casts = [ + 'other_answers' => 'array', // Laravel tự động json_decode + ]; + public function question() { return $this->belongsTo(Question::class, 'question_id'); diff --git a/app/Services/ExerciseService.php b/app/Services/ExerciseService.php index bdfe98e..9c3f08c 100644 --- a/app/Services/ExerciseService.php +++ b/app/Services/ExerciseService.php @@ -2,6 +2,7 @@ namespace App\Services; +use App\Http\Resources\ExerciseResource; use App\Models\Exercise; class ExerciseService @@ -52,4 +53,22 @@ class ExerciseService return $exercise->id; } + + public function detail($id) + { + $exercise = Exercise::with([ + 'skills:id,name', + 'questionGroups' => function ($q) { + $q->select('id', 'exercise_id', 'content'); + }, + 'questionGroups.questions' => function ($q) { + $q->select('id', 'content', 'group_id', 'question_type_id', 'description', 'score', 'explanation', 'hint'); + }, + 'questionGroups.questions.type:id,code,name', + 'questionGroups.questions.choices', + 'questionGroups.questions.blanks', + ])->findOrFail($id); + + return new ExerciseResource($exercise); + } } diff --git a/app/Services/QuestionService.php b/app/Services/QuestionService.php index c5d3b43..4d53950 100644 --- a/app/Services/QuestionService.php +++ b/app/Services/QuestionService.php @@ -6,6 +6,7 @@ use App\Models\Question; use App\Models\QuestionBlank; use App\Models\QuestionChoice; use App\Models\QuestionGroup; +use App\Models\QuestionType; use Illuminate\Support\Facades\DB; class QuestionService @@ -59,19 +60,20 @@ class QuestionService foreach ($data as $groupKey => $value) { $groupId = $groupIds[$groupKey]; foreach ($value->questions as $qIndex => $question) { + $questionType = QuestionType::where('code', $question->type)->first(); + $questionTypeId = $questionType ? $questionType->id : 1; $dataQuestion[] = [ 'exercise_id' => $exerciseId, 'content' => $question->content, 'description' => $question->description ?? null, 'group_id' => $groupId, - 'question_type_id' => 1, + 'question_type_id' => $questionTypeId, 'level' => 2, 'score' => $question->score ?? null, 'explanation' => $question->explanation ?? null, 'hint' => $question->hint ?? null, 'created_at' => $now, - 'updated_at' => $now, - 'custom_key' => "{$groupKey}_{$qIndex}", // dùng để map lại + 'updated_at' => $now ]; $questionMap["{$groupKey}_{$qIndex}"] = [ 'type' => $question->type, diff --git a/routes/api.php b/routes/api.php index 47f394e..495a596 100644 --- a/routes/api.php +++ b/routes/api.php @@ -13,6 +13,7 @@ Route::middleware(['auth:api', 'role:admin'])->get('/user', function (Request $r }); Route::middleware('auth:api')->group(function () { - Route::get('/exercise', [ExerciseController::class, 'index'])->name('exercise.index'); + Route::get('/exercises', [ExerciseController::class, 'index'])->name('exercise.index'); Route::post('/exercise/create', [ExerciseController::class, 'create'])->name('exercise.create'); + Route::get('/exercise/{id}', [ExerciseController::class, 'detail'])->name('exercise.detail'); });