After saving the quiz results, now we can show user how did they do in the quiz, also adding the leaderboard.
First, we need a controller and a route for that.
php artisan make:controller ResultController
routes/web.php:
Route::get('/', [HomeController::class, 'index'])->name('home');Route::get('quiz/{quiz}/{slug?}', [HomeController::class, 'show'])->name('quiz.show');Route::get('results/{test}', [ResultController::class, 'show'])->name('results.show'); // ...
And now after creating the test we can redirect to this page.
app/Livewire/Front/Quizzes/Show.php:
class Show extends Component{ // ... public function submit(): Redirector|RedirectResponse { // ... return to_route('home'); return to_route('results.show', $test); } // ...}
So first, in the controller let's get the test.
app/Http/Controllers/ResultController.php:
class ResultController extends Controller{ public function show(Test $test) { $total_questions = $test->quiz->questions->count(); return view('front.results.show', compact('test')); }}
And let's show the result for the user, but the user row will be shown only for the admin user.
resources/views/front/results/show.blade.php:
<x-app-layout> <div class="py-12"> <div class="mx-auto max-w-7xl sm:px-6 lg:px-8"> <div class="overflow-hidden bg-white shadow-sm sm:rounded-lg"> <div class="p-6 text-gray-900"> <h6 class="text-xl font-bold">My Results</h6> <table class="mt-4 table w-full table-view"> <tbody class="bg-white"> @if(auth()->user()?->is_admin) <tr class="w-28"> <th class="border border-solid bg-slate-100 px-6 py-3 text-left text-sm font-semibold uppercase text-slate-600">User</th> <td class="border border-solid px-6 py-3">{{ $test->user->name ?? '' }} ({{ $test->user->email ?? '' }})</td> </tr> @endif <tr class="w-28"> <th class="border border-solid bg-slate-100 px-6 py-3 text-left text-sm font-semibold uppercase text-slate-600">Date</th> <td class="border border-solid px-6 py-3">{{ $test->created_at ?? '' }}</td> </tr> <tr class="w-28"> <th class="border border-solid bg-slate-100 px-6 py-3 text-left text-sm font-semibold uppercase text-slate-600">Result</th> <td class="border border-solid px-6 py-3"> {{ $test->result }} / {{ $total_questions }} @if($test->time_spent) (time: {{ intval($test->time_spent / 60) }}:{{ gmdate('s', $test->time_spent) }} minutes) @endif </td> </tr> </tbody> </table> </div> </div> </div> </div></x-app-layout>
For now, we see results similar to those below:
Next, let's show questions and answers.
app/Http/Controllers/ResultController.php:
class ResultController extends Controller{ public function show(Test $test) { $total_questions = $test->quiz->questions->count(); $results = TestAnswer::where('test_id', $test->id) ->with('question.questionOptions') ->get(); return view('front.results.show', compact('test', 'results')); }}
resources/views/front/results/show.blade.php:
<x-app-layout> // .. <div class="max-w-7xl mx-auto sm:px-6 lg:px-8"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="p-6 text-gray-900"> @foreach($results as $result) <table class="table table-view w-full my-4 bg-white"> <tbody class="bg-white"> <tr class="bg-slate-100"> <td class="w-1/2">Question #{{ $loop->iteration }}</td> <td>{!! nl2br($result->question->question_text) !!}</td> </tr> <tr> <td>Options</td> <td> @foreach($result->question->questionOptions as $option) <li @class([ 'underline' => $result->option_id == $option->id, 'font-bold' => $option->correct == 1, ])> {{ $option->option }} @if ($option->correct == 1) <span class="italic">(correct answer)</span> @endif @if ($result->option_id == $option->id) <span class="italic">(your answer)</span> @endif </li> @endforeach @if(is_null($result->option_id)) <span class="font-bold italic">Question unanswered.</span> @endif </td> </tr> @if($result->question->answer_explanation || $result->question->more_info_link) <tr> <td>Answer Explanation</td> <td> {!! $result->question->answer_explanation !!} @if ($result->question->more_info_link) <div class="mt-4"> Read more: <a href="{{ $result->question->more_info_link }}" class="hover:underline" target="_blank"> {{ $result->question->more_info_link }} </a> </div> @endif </td> </tr> @endif </tbody> </table> @if(!$loop->last) <hr class="my-4 md:min-w-full"> @endif @endforeach </div> </div> </div></x-app-layout>
The result now we have is similar to the one below:
Next, let's add the last "card" where we will show the leaderboard. But it will be only shown if the quiz isn't public.
app/Http/Controllers/ResultController.php:
<?php namespace App\Http\Controllers; use App\Models\Test;use App\Models\User;use App\Models\TestAnswer; class ResultController extends Controller{ public function show(Test $test) { $test->load('user', 'quiz'); $total_questions = $test->quiz->questions->count(); $users = null; $results = TestAnswer::where('test_id', $test->id) ->with('question.questionOptions') ->get(); if ($test->quiz->public == 0) { $users = User::select('users.id', 'users.name', \DB::raw('sum(tests.result) as correct'), \DB::raw('sum(tests.time_spent) as time_spent')) ->join('tests', 'users.id', '=', 'tests.user_id') ->where('tests.quiz_id', $test->quiz_id) ->whereNotNull('tests.time_spent') ->groupBy('users.id', 'users.name') ->orderBy('correct', 'desc') ->orderBy('time_spent') ->get(); } return view('front.results.show', compact('test', 'results', 'total_questions', 'users')); }}
We'll add a leaderboard "card" between My Results and Questions and Answers.
resources/views/front/results/show.blade.php:
<x-app-layout>// ...@if($users) <div class="max-w-7xl mx-auto sm:px-6 lg:px-8 pb-12"> <div class="bg-white overflow-hidden shadow-sm sm:rounded-lg"> <div class="p-6 text-gray-900"> <h6 class="text-xl font-bold">Leaderboard</h6> <table class="table mt-4 w-full table-view"> <thead> <tr> <th></th> <th>Username</th> <th>Correct answers</th> </tr> </thead> <tbody class="bg-white"> @forelse($users as $user) <tr @class(['bg-slate-100' => auth()->user() && auth()->user()->name == $user->name])> <td class="w-9">{{ $loop->iteration }}</td> <td class="w-1/2">{{ $user->name }}</td> <td>{{ $user->correct }} / {{ $total_questions }} (time: {{ intval($user->time_spent / 60) }}:{{ gmdate('s', $user->time_spent) }} minutes)</td> </tr> @empty <tr> <td colspan="3">No results</td> </tr> @endforelse </tbody> </table> </div> </div> </div>@endif// ...</x-app-layout>
If the quiz isn't public you should see a similar result: