Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

Kikismedia's avatar

js affecting livewire option

i need help fixing this when the timer counts down it shuffles the quiz option and i dont want that

<div>
    @if ($attempt)
        <div class="bg-teal-200 py-3 rounded-md text-gray-600 text-center">
            <p class="font-semibold">You have already attempted this quiz.</p>
            <div class="mt-2">
                <a href="{{route('results.show' , $attempt)}}" class="bg-teal-500 rounded-md px-4 py-2 text-white">Check Result</a>
            </div>
        </div>
    @else
        <div class="mb-2">
            Time left for this quiz: <span class="font-bold">{{ $timer }}</span> sec.
        </div>

        <div>
            <span class="text-bold">Question {{ $currentQuestionIndex + 1 }} of {{ $this->questionsCount }}:</span>
            <h2 class="mb-4 text-2xl">{{ $currentQuestion->question_text }}</h2>

            @foreach ($currentQuestion->questionOptions as $option)
                <div wire:key="{{ $option->id }}">
                    <label for="option.{{ $option->id }}">
                        <input type="radio" id="option.{{ $option->id }}"
                            wire:model.defer="questionsAnswers.{{ $currentQuestionIndex }}"
                            name="questionsAnswers.{{ $currentQuestionIndex }}" value="{{ $option->id }}">
                        {{ $option->option }}
                    </label>
                </div>
            @endforeach

            @if ($currentQuestionIndex < $this->questionsCount - 1)
                <div class="mt-4">
                    <x-secondary-button wire:click="changeQuestion"
                        x-on:click="timer = {{ config('quiz.secondsPerQuestion') }};">
                        Next question
                    </x-secondary-button>
                </div>
            @else
                <div class="mt-4">
                    <x-primary-button wire:click="submit">Submit</x-primary-button>
                </div>
            @endif
        </div>


        <script>
            setInterval(() => {
                @this.call('updateTimer');
            }, 1000);
        </script>

    @endif
</div>

livewire component

<?php

namespace App\Http\Livewire\Front\Quizzes;

use App\Models\Quiz;
use App\Models\Test;
use Livewire\Component;
use App\Models\Question;
use App\Models\TestAnswer;
use App\Models\QuestionOption;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;

class Show extends Component
{
    public Quiz $quiz;
    public Collection $questions;
    public Question $currentQuestion;
    public int $currentQuestionIndex = 0;
    public array $questionsAnswers = [];
    public int $timer = 0;
    public $attempt;

    public function render(): View
    {
        // Check if the user has already attempted the quiz

        return view('livewire.front.quizzes.show');
    }

    public function mount()
    {

        $this->attempt = Test::where('user_id', auth()->id())
            ->where('quiz_id', $this->quiz->id)
            ->first();


        $this->timer = config('quiz.secondsPerQuestion');

        // User hasn't attempted the quiz, proceed with initializing the quiz
        $this->questions = Question::query()
            ->inRandomOrder()
            ->whereRelation('quizzes', 'quiz_id', $this->quiz->id)
            ->with('questionOptions')
            ->get();

        $this->currentQuestion = $this->questions[$this->currentQuestionIndex];

        for ($i = 0; $i < $this->questionsCount; $i++) {
            $this->questionsAnswers[$i] = null;
        }
    }

    public function getQuestionsCountProperty(): int
    {
        return $this->questions->count();
    }

    public function changeQuestion()
    {
        $this->currentQuestionIndex++;

        if ($this->currentQuestionIndex >= $this->questionsCount) {
            return $this->submit();
        }

        $this->currentQuestion = $this->questions[$this->currentQuestionIndex];
        //$this->timer = config('quiz.secondsPerQuestion');
    }

    public function updateTimer()
    {
        if ($this->timer > 0) {
            $this->timer--;
        } else {
            $this->submit();
        }
    }

    public function submit()
    {
        $result = 0;

        $test = Test::create([
            'user_id' => auth()->id(),
            'quiz_id' => $this->quiz->id,
            'result' => 0,
            'ip_address' => request()->ip(),
        ]);

        foreach ($this->questionsAnswers as $key => $option) {
            $status = 0;

            if ($option !== null && 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,
                'correct' => $status,
            ]);
        }

        $test->update([
            'result' => $result,
        ]);

        return redirect()->route('results.show', $test);
    }
}
0 likes
6 replies
Snapey's avatar

Try changing the name of your function.

updateFoo is a built in method, called when Foo is changing.

You are also using this as a method called by the front end.

Kikismedia's avatar

@Snapey You mean like this, this did not work it still shuffles

public function mount()
{
    $this->attempt = Test::where('user_id', auth()->id())
        ->where('quiz_id', $this->quiz->id)
        ->first();

    $this->timer = config('quiz.secondsPerQuestion');

    // User hasn't attempted the quiz, proceed with initializing the quiz
    $questionIds = collect(Question::query()
        ->inRandomOrder()
        ->whereRelation('quizzes', 'quiz_id', $this->quiz->id)
        ->pluck('id')
    );

    $this->questions = Question::whereIn('id', $questionIds)
        ->with('questionOptions')
        ->get();

    $this->currentQuestion = $this->questions[$this->currentQuestionIndex];

    for ($i = 0; $i < $this->questionsCount; $i++) {
        $this->questionsAnswers[$i] = null;
    }
}

And this

<script>
            setInterval(() => {
                @this.call('deTimer');
            }, 1000);
        </script>
Snapey's avatar

@Kikismedia do you need all questions?

$this->questions = Question::whereIn('id', $questionIds)
        ->with('questionOptions')
        ->get();

This will not respect the order in the array

Please or to participate in this conversation.