In this tutorial, we will add the ability to add the possible answer options to the question. This is the first time in this course where Livewire will actually shine:
Let's start by creating a Model with Migration and adding relationships.
php artisan make:model QuestionOption -m
database/migrations/xxxx_create_question_options_table.php:
return new class extends Migration{ public function up(): void { Schema::create('question_options', function (Blueprint $table) { $table->id(); $table->string('option'); $table->boolean('correct')->default(0)->nullable(); $table->foreignId('question_id')->nullable()->constrained(); $table->timestamps(); $table->softDeletes(); }); }};
app/Models/QuestionOption.php:
class QuestionOption extends Model{ use HasFactory; use SoftDeletes; protected $fillable = [ 'option', 'correct', 'question_id', ]; protected $casts = [ 'correct' => 'boolean', ]; public function question(): BelongsTo { return $this->belongsTo(Question::class); }}
To the QuestionOption
Model we also add a Cast to the correct
field, so that it would always return a boolean.
class Question extends Model{ use SoftDeletes; protected $fillable = [ 'question_text', 'code_snippet', 'answer_explanation', 'more_info_link', ]; public function questionOptions(): HasMany { return $this->hasMany(QuestionOption::class)->inRandomOrder(); } }
Next, we need to extend the Livewire component for the questions form that we created in an earlier lesson.
app/Livewire/Questions/QuestionForm.php:
class QuestionForm extends Component{ public Question $question; public bool $editing = false; public array $questionOptions = []; public function mount(Question $question): void { $this->question = $question; if ($this->question->exists) { $this->editing = true; foreach ($question->questionOptions as $option) { $this->questionOptions[] = [ 'id' => $option->id, 'option' => $option->option, 'correct' => $option->correct, ]; } } } public function addQuestionsOption(): void { $this->questionOptions[] = [ 'option' => '', 'correct' => false ]; } public function removeQuestionsOption(int $index): void { unset($this->questionOptions[$index]); $this->questionOptions = array_values(($this->questionOptions)); } public function save(): Redirector { $this->validate(); if (empty($this->question)) { $this->question = Question::create($this->only(['question_text', 'code_snippet', 'answer_explanation', 'more_info_link'])); } else { $this->question->update($this->only(['question_text', 'code_snippet', 'answer_explanation', 'more_info_link'])); } $this->question->questionOptions()->delete(); foreach ($this->questionOptions as $option) { $this->question->questionOptions()->create($option); } return to_route('questions'); } public function render(): View { return view('livewire.questions.form'); } protected function rules(): array { return [ 'question_text' => [ 'string', 'required', ], 'code_snippet' => [ 'string', 'nullable', ], 'answer_explanation' => [ 'string', 'nullable', ], 'more_info_link' => [ 'url', 'nullable', ], 'questionOptions' => [ 'required', 'array', ], 'questionOptions.*.option' => [ 'required', 'string', ], ]; }}
Now, what have we added here?
- First, the
$questionOptions
public property, where all options will be added. - In the
mount()
where we check ifQuestion
exists, we add code to add all options into the$questionOptions
public array property. - Next, two methods
addQuestionsOption()
andremoveQuestionsOption()
. Both the name says what they do. First, adds a new question option to the array, and second, removes and indexes an array. - In the
save()
method we added the creation of the options for the questions. - And last, additional rules for question options.
Now, let's show question options in the form. We will add it right after the question text.
resources/views/livewire/questions/form.blade.php:
// ...<div class="mt-4"> <x-input-label for="question_options" value="Question options"/> @foreach($questionOptions as $index => $questionOption) <div class="flex mt-2"> <x-text-input type="text" wire:model="questionOptions.{{ $index }}.option" class="w-full" name="questions_options_{{ $index }}" id="questions_options_{{ $index }}" autocomplete="off"/> <div class="flex items-center"> <input type="checkbox" class="mr-1 ml-4" wire:model.defer="questionOptions.{{ $index }}.correct"> Correct <button wire:click="removeQuestionsOption({{ $index }})" type="button" class="ml-4 rounded-md border border-transparent bg-red-200 px-4 py-2 text-xs uppercase text-red-500 hover:bg-red-300 hover:text-red-700"> Delete </button> </div> </div> <x-input-error :messages="$errors->get('questionOptions.' . $index . '.option')" class="mt-2" /> @endforeach <x-input-error :messages="$errors->get('questionOptions')" class="mt-2" /> <x-primary-button wire:click="addQuestionsOption" type="button" class="mt-2"> Add </x-primary-button></div> // ...
After visiting create the form you should see a button ADD
, and after pressing it you should see appearing input for adding an option to a question.
Now you can create questions with question options.
Hello, I cant get this code to work! Follow all the processes.
Hi, the reference
resources/views/livewire/questions/form.blade.php
shoud be
resources/views/livewire/questions/question-form.blade.php
Many thanks,
In my case, I missed out the livewire asset and script tag in the app.blade.php.
Regards
Thanks @asequeira well noticed, fixed in the lesson text!
Hi, I want the repository link please Thanks
https://github.com/LaravelDaily/Livewire-Laraquiz-Course
Hi many thanks for this good offer. When I login I don't see the question form. How do I find it? I did this "/questions/create" but still did not find it
What question form are you talking about?
Hi please im getting error . Livewire page component layout view not found: [components.layouts.app]
i followed the direction but cant acces question but the above error
Set the proper layout in the config https://livewire.laravel.com/docs/components#global-layout-configuration