Back to Course |
Creating a Quiz System with Laravel 10 + Livewire 3: Step-by-Step

Saving Quiz/Test Results

Now that user can take quizzes we need to save results in the DB which we will do in this tutorial.

For this we will need two Eloquent Models:

  • Test where we will save information about user, which test (another word for "quiz" but it's broader) he took, how much time it took him.
  • TestAnswer where we will store the information about which answers user chose during the quiz.
php artisan make:model Test -m

database/migrations/xxxx_create_tests_table.php:

return new class extends Migration
{
public function up(): void
{
Schema::create('tests', function (Blueprint $table) {
$table->id();
$table->integer('result')->nullable();
$table->string('ip_address')->nullable();
$table->integer('time_spent')->nullable();
$table->foreignId('user_id')->nullable()->constrained();
$table->foreignId('quiz_id')->nullable()->constrained();
$table->timestamps();
$table->softDeletes();
});
}
// ...
};

app/Models/Test.php:

class Test extends Model
{
use HasFactory;
use SoftDeletes;
 
protected $fillable = [
'user_id',
'quiz_id',
'result',
'ip_address',
'time_spent',
];
 
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
 
public function quiz(): BelongsTo
{
return $this->belongsTo(Quiz::class);
}
 
public function questions(): BelongsToMany
{
return $this->belongsToMany(Question::class, 'test_answers', 'test_id', 'question_id');
}
}

database/migrations/xxxx_create_test_answers_table.php:

return new class extends Migration
{
public function up(): void
{
Schema::create('test_answers', function (Blueprint $table) {
$table->id();
$table->boolean('correct')->default(0)->nullable();
$table->foreignId('user_id')->nullable()->constrained();
$table->foreignId('test_id')->nullable()->constrained();
$table->foreignId('question_id')->nullable()->constrained();
$table->foreignId('option_id')->nullable()->references('id')->on('question_options');
$table->timestamps();
$table->softDeletes();
});
}
// ...
};

app/Models/TestAnswer.php:

class TestAnswer extends Model
{
use HasFactory;
use SoftDeletes;
 
protected $fillable = [
'user_id',
'test_id',
'question_id',
'option_id',
'correct',
];
 
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
 
public function test(): BelongsTo
{
return $this->belongsTo(Test::class);
}
 
public function question(): BelongsTo
{
return $this->belongsTo(Question::class);
}
 
public function option(): BelongsTo
{
return $this->belongsTo(QuestionOption::class);
}
}

Now after submitting quiz we can save test results.

In the submit method first we set a $result to 0. We will increase it if users answer will be correct. And then we first create a new test.

app/Livewire/Front/Quizzes/Show.php:

class Show extends Component
{
// ...
 
public function submit()
{
$result = 0;
 
$test = Test::create([
'user_id' => auth()->id(),
'quiz_id' => $this->quiz->id,
'result' => 0,
'ip_address' => request()->ip(),
'time_spent' => now()->timestamp - $this->startTimeSeconds
]);
}
 
// ...
}

Then we need to go through every answered question. In this foreach first we set $status to 0 which means answer isn't correct. Then we need to find that question option and if it is correct we $status to 1 (or true) and increase $result. And create a new TestAnswer.

app/Livewire/front/quizzes/Show.php:

class Show extends Component
{
// ...
 
public function submit(): Redirector
{
$result = 0;
 
$test = Test::create([
'user_id' => auth()->id(),
'quiz_id' => $this->quiz->id,
'result' => 0,
'ip_address' => request()->ip(),
'time_spent' => now()->timestamp - $this->startTimeSeconds
]);
 
foreach ($this->questionsAnswers as $key => $option) {
$status = 0;
 
if (! empty($option) && QuestionOption::find($option)->correct) {
$status = 1;
$result++;
}
 
TestAnswer::create([
'user_id' => auth()->id(),
'test_id' => $test->id,
'question_id' => $this->questions[$key]->id,
'option_id' => $option ?? null,
'correct' => $status,
]);
}
}
 
// ..
}

After creating Test with the Answers all that is left to update test with the result and redirect. For now we will redirect to homepage.

app/Livewire/Front/Quizzes/Show.php:

class Show extends Component
{
// ...
public function submit(): Redirector|RedirectResponse
{
$result = 0;
 
$test = Test::create([
'user_id' => auth()->id(),
'quiz_id' => $this->quiz->id,
'result' => 0,
'ip_address' => request()->ip(),
'time_spent' => now()->timestamp - $this->startTimeSeconds
]);
 
foreach ($this->questionsAnswers as $key => $option) {
$status = 0;
 
if (! empty($option) && QuestionOption::find($option)->correct) {
$status = 1;
$result++;
}
 
TestAnswer::create([
'user_id' => auth()->id(),
'test_id' => $test->id,
'question_id' => $this->questions[$key]->id,
'option_id' => $option ?? null,
'correct' => $status,
]);
}
 
$test->update([
'result' => $result,
]);
 
return to_route('home');
}
// ...
}