前のページ

「クイズアプリ③」ユーザーがクイズの回答をする(Laravel12)

今回はユーザーがクイズに回答できるようにします。

クイズに回答すると正解・不正解の表示をさせます。


web.phpの修正

welcome.blade.phpを表示するweb.phpの記述は現在下記です。

Route::get('/', function () {
    return view('welcome');
});

welcome.blade.phpに変数を渡したいのでQuizController.phpにshowアクションを作成してそれで表示する為にweb.phpのコードを下記に変更します。

Route::middleware('auth')->group(function () {
    Route::controller(QuizController::class)->group(function() {
        Route::get('/quiz/create', 'create')->name('quiz.create');

        Route::post('/quiz/store', 'store')->name('quiz.store');

        Route::get('/quiz/admin', 'admin')->name('quiz.admin');

        Route::delete('/quiz/{question}/delete', 'delete')->name('quiz.delete');

        Route::get('/quiz/{question}/edit', 'edit')->name('quiz.edit');

        Route::put('/quiz/{question}/update', 'update')->name('quiz.update');


        //ここから追加
        Route::get('/', 'index')->name('welcome');
        
        Route::get('/quiz/{question}/show', 'show')->name('quiz.show');
        //ここまで追加


    });
});

showアクションもwelcome.blade.phpに必要なので記述しています。

QuizController.phpにindexアクションの記述をします。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Question;

class QuizController extends Controller{
    public function create()
    {
        return view('quiz.create');
    }

    public function store(Request $request)
    {
        $question_table = new Question();

        $question_table->question = $request->question;

        $question_table->choices = $request->choices;

        $question_table->correct_choice = $request->correct_choice;

        $question_table->save();

        return back()->with('message', 'クイズを作成しました');
    }

    public function admin()
    {
        $quizs = Question::all();

        return view('quiz.admin', compact('quizs'));
    }

    public function delete(Question $question)
    {
        $question->delete();

        return back()->with('message', 'クイズを削除しました');
    }

    public function edit(Question $question)
    {
        return view('quiz.edit', compact('question'));
    }

    public function update(Request $request, Question $question)
    {
        $question->question = $request->question;

        $question->choices = $request->choices;

        $question->correct_choice = $request->correct_choice;

        $question->save();
        
        return back()->with('message', 'クイズを更新しました');
    }


    // ここから追加
    public function index()
    {
        $question = Question::first();

        return view('welcome', compact('question'));
    }

    public function show(Question $question)
    {
        return view('quiz.show', compact('question'));
    }
    // ここまで追加
}

「Question::first()」はquestionsテーブルの最初のレコードのデータを取得するという意味です。

welcome.blade.phpのコードを下記にします。

<!DOCTYPE html><html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
    <head>
        <meta charset="utf-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">

        <title>Laravel</title>

        <!-- Fonts -->
        <link rel="preconnect" href="https://fonts.bunny.net">
        <link href="https://fonts.bunny.net/css?family=figtree:400,600&display=swap" rel="stylesheet" />

        <!-- Styles -->
        <style>
            /* ! tailwindcss v3.2.4 | MIT License | https://tailwindcss.com */*,::after,::before{box-sizing:border-box;border-width:0;border-style:solid;border-color:#e5e7eb}::after,::before{--tw-content:''}html{line-height:1.5;-webkit-text-size-adjust:100%;-moz-tab-size:4;tab-size:4;font-family:Figtree, sans-serif;font-feature-settings:normal}body{margin:0;line-height:inherit}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;font-weight:inherit;line-height:inherit;color:inherit;margin:0;padding:0}button,select{text-transform:none}[type=button],[type=reset],[type=submit],button{-webkit-appearance:button;background-color:transparent;background-image:none}:-moz-focusring{outline:auto}:-moz-ui-invalid{box-shadow:none}progress{vertical-align:baseline}::-webkit-inner-spin-button,::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}fieldset{margin:0;padding:0}legend{padding:0}menu,ol,ul{list-style:none;margin:0;padding:0}textarea{resize:vertical}input::placeholder,textarea::placeholder{opacity:1;color:#9ca3af}[role=button],button{cursor:pointer}:disabled{cursor:default}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{max-width:100%;height:auto}[hidden]{display:none}*, ::before, ::after{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::-webkit-backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }::backdrop{--tw-border-spacing-x:0;--tw-border-spacing-y:0;--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;--tw-scale-x:1;--tw-scale-y:1;--tw-pan-x: ;--tw-pan-y: ;--tw-pinch-zoom: ;--tw-scroll-snap-strictness:proximity;--tw-ordinal: ;--tw-slashed-zero: ;--tw-numeric-figure: ;--tw-numeric-spacing: ;--tw-numeric-fraction: ;--tw-ring-inset: ;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgb(59 130 246 / 0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000;--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;--tw-blur: ;--tw-brightness: ;--tw-contrast: ;--tw-grayscale: ;--tw-hue-rotate: ;--tw-invert: ;--tw-saturate: ;--tw-sepia: ;--tw-drop-shadow: ;--tw-backdrop-blur: ;--tw-backdrop-brightness: ;--tw-backdrop-contrast: ;--tw-backdrop-grayscale: ;--tw-backdrop-hue-rotate: ;--tw-backdrop-invert: ;--tw-backdrop-opacity: ;--tw-backdrop-saturate: ;--tw-backdrop-sepia: }.relative{position:relative}.mx-auto{margin-left:auto;margin-right:auto}.mx-6{margin-left:1.5rem;margin-right:1.5rem}.ml-4{margin-left:1rem}.mt-16{margin-top:4rem}.mt-6{margin-top:1.5rem}.mt-4{margin-top:1rem}.-mt-px{margin-top:-1px}.mr-1{margin-right:0.25rem}.flex{display:flex}.inline-flex{display:inline-flex}.grid{display:grid}.h-16{height:4rem}.h-7{height:1.75rem}.h-6{height:1.5rem}.h-5{height:1.25rem}.min-h-screen{min-height:100vh}.w-auto{width:auto}.w-16{width:4rem}.w-7{width:1.75rem}.w-6{width:1.5rem}.w-5{width:1.25rem}.max-w-7xl{max-width:80rem}.shrink-0{flex-shrink:0}.scale-100{--tw-scale-x:1;--tw-scale-y:1;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.grid-cols-1{grid-template-columns:repeat(1, minmax(0, 1fr))}.items-center{align-items:center}.justify-center{justify-content:center}.gap-6{gap:1.5rem}.gap-4{gap:1rem}.self-center{align-self:center}.rounded-lg{border-radius:0.5rem}.rounded-full{border-radius:9999px}.bg-gray-100{--tw-bg-opacity:1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.bg-white{--tw-bg-opacity:1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-red-50{--tw-bg-opacity:1;background-color:rgb(254 242 242 / var(--tw-bg-opacity))}.bg-dots-darker{background-image:url("data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.22676 0C1.91374 0 2.45351 0.539773 2.45351 1.22676C2.45351 1.91374 1.91374 2.45351 1.22676 2.45351C0.539773 2.45351 0 1.91374 0 1.22676C0 0.539773 0.539773 0 1.22676 0Z' fill='rgba(0,0,0,0.07)'/%3E%3C/svg%3E")}.from-gray-700\/50{--tw-gradient-from:rgb(55 65 81 / 0.5);--tw-gradient-to:rgb(55 65 81 / 0);--tw-gradient-stops:var(--tw-gradient-from), var(--tw-gradient-to)}.via-transparent{--tw-gradient-to:rgb(0 0 0 / 0);--tw-gradient-stops:var(--tw-gradient-from), transparent, var(--tw-gradient-to)}.bg-center{background-position:center}.stroke-red-500{stroke:#ef4444}.stroke-gray-400{stroke:#9ca3af}.p-6{padding:1.5rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.text-center{text-align:center}.text-right{text-align:right}.text-xl{font-size:1.25rem;line-height:1.75rem}.text-sm{font-size:0.875rem;line-height:1.25rem}.font-semibold{font-weight:600}.leading-relaxed{line-height:1.625}.text-gray-600{--tw-text-opacity:1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity:1;color:rgb(17 24 39 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity:1;color:rgb(107 114 128 / var(--tw-text-opacity))}.underline{-webkit-text-decoration-line:underline;text-decoration-line:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.shadow-2xl{--tw-shadow:0 25px 50px -16px rgb(0 0 0 / 0.25);--tw-shadow-colored:0 25px 50px -16px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)}.shadow-gray-500\/20{--tw-shadow-color:rgb(107 114 128 / 0.2);--tw-shadow:var(--tw-shadow-colored)}.transition-all{transition-property:all;transition-timing-function:cubic-bezier(0.4, 0, 0.2, 1);transition-duration:150ms}.selection\:bg-red-500 *::selection{--tw-bg-opacity:1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.selection\:text-white *::selection{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity))}.selection\:bg-red-500::selection{--tw-bg-opacity:1;background-color:rgb(239 68 68 / var(--tw-bg-opacity))}.selection\:text-white::selection{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity))}.hover\:text-gray-900:hover{--tw-text-opacity:1;color:rgb(17 24 39 / var(--tw-text-opacity))}.hover\:text-gray-700:hover{--tw-text-opacity:1;color:rgb(55 65 81 / var(--tw-text-opacity))}.focus\:rounded-sm:focus{border-radius:0.125rem}.focus\:outline:focus{outline-style:solid}.focus\:outline-2:focus{outline-width:2px}.focus\:outline-red-500:focus{outline-color:#ef4444}.group:hover .group-hover\:stroke-gray-600{stroke:#4b5563}.z-10{z-index: 10}@media (prefers-reduced-motion: no-preference){.motion-safe\:hover\:scale-\[1\.01\]:hover{--tw-scale-x:1.01;--tw-scale-y:1.01;transform:translate(var(--tw-translate-x), var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}}@media (prefers-color-scheme: dark){.dark\:bg-gray-900{--tw-bg-opacity:1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}.dark\:bg-gray-800\/50{background-color:rgb(31 41 55 / 0.5)}.dark\:bg-red-800\/20{background-color:rgb(153 27 27 / 0.2)}.dark\:bg-dots-lighter{background-image:url("data:image/svg+xml,%3Csvg width='30' height='30' viewBox='0 0 30 30' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1.22676 0C1.91374 0 2.45351 0.539773 2.45351 1.22676C2.45351 1.91374 1.91374 2.45351 1.22676 2.45351C0.539773 2.45351 0 1.91374 0 1.22676C0 0.539773 0.539773 0 1.22676 0Z' fill='rgba(255,255,255,0.07)'/%3E%3C/svg%3E")}.dark\:bg-gradient-to-bl{background-image:linear-gradient(to bottom left, var(--tw-gradient-stops))}.dark\:stroke-gray-600{stroke:#4b5563}.dark\:text-gray-400{--tw-text-opacity:1;color:rgb(156 163 175 / var(--tw-text-opacity))}.dark\:text-white{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:shadow-none{--tw-shadow:0 0 #0000;--tw-shadow-colored:0 0 #0000;box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000), var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow)}.dark\:ring-1{--tw-ring-offset-shadow:var(--tw-ring-inset) 0 0 0 var(--tw-ring-offset-width) var(--tw-ring-offset-color);--tw-ring-shadow:var(--tw-ring-inset) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color);box-shadow:var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow, 0 0 #0000)}.dark\:ring-inset{--tw-ring-inset:inset}.dark\:ring-white\/5{--tw-ring-color:rgb(255 255 255 / 0.05)}.dark\:hover\:text-white:hover{--tw-text-opacity:1;color:rgb(255 255 255 / var(--tw-text-opacity))}.group:hover .dark\:group-hover\:stroke-gray-400{stroke:#9ca3af}}@media (min-width: 640px){.sm\:fixed{position:fixed}.sm\:top-0{top:0px}.sm\:right-0{right:0px}.sm\:ml-0{margin-left:0px}.sm\:flex{display:flex}.sm\:items-center{align-items:center}.sm\:justify-center{justify-content:center}.sm\:justify-between{justify-content:space-between}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width: 768px){.md\:grid-cols-2{grid-template-columns:repeat(2, minmax(0, 1fr))}}@media (min-width: 1024px){.lg\:gap-8{gap:2rem}.lg\:p-8{padding:2rem}}
        </style>
    </head>
    <body class="antialiased">
        <div class="relative sm:flex sm:justify-center sm:items-center min-h-screen bg-dots-darker bg-center bg-gray-100 dark:bg-dots-lighter dark:bg-gray-900 selection:bg-red-500 selection:text-white">
            @if (Route::has('login'))
                <div class="sm:fixed sm:top-0 sm:right-0 p-6 text-right z-10">
                    @auth
                        <a href="{{ url('/dashboard') }}" class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Dashboard</a>
                    @else
                        <a href="{{ route('login') }}" class="font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Log in</a>

                        @if (Route::has('register'))
                            <a href="{{ route('register') }}" class="ml-4 font-semibold text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white focus:outline focus:outline-2 focus:rounded-sm focus:outline-red-500">Register</a>
                        @endif
                    @endauth
                </div>
            @endif

            <div class="max-w-7xl mx-auto p-6 lg:p-8">
                <h2 class="font-bold">3択クイズ</h2>
                <a class="answer" href="{{ route('quiz.show', ['question' => $question->id]) }}">クイズに答える</a>       //この行を修正
                @auth
                    @if (auth()->user()->role_type_id == 1)
                        <a class="manage" href="{{ route('quiz.admin') }}">管理人用</a>
                    @endif
                @endauth
            </div>
        </div>
    </body></html>

<style>
    .answer, .manage{
        text-align: center;
        display: block;
        text-decoration:underline;
        margin-bottom: 20px;
        margin-top: 20px;
    }
    h2{
        font-size: 20px;
        font-weight: bold;
        text-align:center;
    }
</style>

それではクイズのページに入ることができるようにします。


クイズのページ

web.phpとQuizController.phpの記述はしたのでquizフォルダの下にshow.blade.phpを作成して下記の記述をします。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>問題</title>
    </head>
    <body>
        <div class="container">
            <form action="{{ route('quiz.answer', ['question' => $question->id]) }}" method="post">
                @csrf
                <h2>問題 : {{ $question->question }}</h2>
                @foreach($question->choices as $index => $choice)
                    <div>
                        <input type="radio" name="choice" value="{{ $index + 1 }}" required>
                        {{ $choice }}
                    </div>
                @endforeach

                <input type="hidden" name="question_id" value="{{ $question->id }}">
                <button type="submit">回答する</button>
            </form>
        </div>
    </body>
</html>

<style>
    .container{
        width: 70%;
        margin-left:auto;
        margin-right:auto;
        padding-top: 50px;
    }
    .input{
        width: 60%;
        display: inline-block;
        margin-bottom: 20px;
    }
    button{
        margin-top: 20px;
    }
</style>

今の状態で画面に入ると「Route [quiz.answer] not defined.」のエラーが表示されるのでquizフォルダの下にanswer.blade.phpを作成してから適当に記述します、とりあえず「でも」と記述します。

また、web.phpに下記の記述をします。

Route::middleware('auth')->group(function () {
    Route::controller(QuizController::class)->group(function() {
        Route::get('/quiz/create', 'create')->name('quiz.create');

        Route::post('/quiz/store', 'store')->name('quiz.store');

        Route::get('/quiz/admin', 'admin')->name('quiz.admin');

        Route::delete('/quiz/{question}/delete', 'delete')->name('quiz.delete');

        Route::get('/quiz/{question}/edit', 'edit')->name('quiz.edit');

        Route::put('/quiz/{question}/update', 'update')->name('quiz.update');

        Route::get('/', 'index')->name('welcome');
        
        Route::get('/quiz/{question}/show', 'show')->name('quiz.show');

        Route::post('/quiz/{question}/answer', 'answer')->name('quiz.answer');       //この行を追加
    });
});

「@foreach ($question->choices as $index => $choice)」の記述が初めて登場しました。

「$index =>」を記述すると「$choice」に対するインデックス番号を付属することができます。

最初の値は0です。

下記みたいな表示になるはずです。(問題文と回答内容は登録した値によって変わります)

20241231_155402_quiz13.jpg

次は回答のページを実装します。


解答のページ

QuizController.phpに下記の記述をします。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Question;

class QuizController extends Controller{
    public function create()
    {
        return view('quiz.create');
    }

    public function store(Request $request)
    {
        $question_table = new Question();

        $question_table->question = $request->question;

        $question_table->choices = $request->choices;

        $question_table->correct_choice = $request->correct_choice;

        $question_table->save();

        return back()->with('message', 'クイズを作成しました');
    }

    public function admin()
    {
        $quizs = Question::all();

        return view('quiz.admin', compact('quizs'));
    }

    public function delete(Question $question)
    {
        $question->delete();

        return back()->with('message', 'クイズを削除しました');
    }

    public function edit(Question $question)
    {
        return view('quiz.edit', compact('question'));
    }

    public function update(Request $request, Question $question)
    {
        $question->question = $request->question;

        $question->choices = $request->choices;

        $question->correct_choice = $request->correct_choice;

        $question->save();
        
        return back()->with('message', 'クイズを更新しました');
    }

    public function index()
    {
        $question = Question::first();

        return view('welcome', compact('question'));
    }

    public function show(Question $question)
    {
        return view('quiz.show', compact('question'));
    }


    // ここから追加
    public function answer(Question $question, Request $request)
    {
        $user_answer = $request->input('choice');

        $correct_choice_index = $question->correct_choice;

        $is_correct = false;

        if ($user_answer == $correct_choice_index) {
            $is_correct = true;
        }

        return view('quiz.answer', compact('question', 'user_answer', 'correct_choice_index', 'is_correct'));
    }
    // ここまで追加

}

「$request->input('choice')」は初めて登場したので解説します。

クイズのページに下記の記述がありましたが回答のページで「{{ $choice }}」を使って正解の判定をしないといけません。

<form action="{{ route('quiz.answer', ['question' => $question->id]) }}" method="post">
    @csrf
    <h2>問題 : {{ $question->question }}</h2>
    @foreach($question->choices as $index => $choice)
        <div>
            <input type="radio" name="choice" value="{{ $index + 1 }}" required>
            {{ $choice }}           //←これ
        </div>
    @endforeach

    <input type="hidden" name="question_id" value="{{ $question->id }}">
    <button type="submit">回答する</button>
</form>

クイズのページの「{{ $choice }}」の値を解答のページで受け取る為の記述が「$request->input('choice')」です、$choiceの$は取ります。
if文の記述は正解か不正解かを判定する為の記述です。

次はビューです。

answer.blade.phpの記述を下記に変更します。

<!DOCTYPE html>
<html lang="ja">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>回答</title>
    </head>
    <body>
        <div class="container">
            <h2>問題{{ $question->id }}の結果</h2>
            <p>あなたは選択肢{{ $user_answer }}を選びました。</p>
            @if ($is_correct)
                <p class="answer">正解です!</p>
            @else
                <p><span class="answer">不正解です。</span>正解は{{ $question->choices[$question->correct_choice - 1]}}です。</p>
            @endif
        </div>
    </body>
</html>

<style>
    .container{
        width: 70%;
        margin-left:auto;
        margin-right:auto;
    }
    .input{
        width: 60%;
        display: inline-block;
        margin-bottom: 20px;
    }
    .answer{
        font-weight: bold;
    }
</style>

 下記は不正解の場合ですがこういう感じで正解・不正解が表示されます。

20241231_165437_quiz14.jpg

今のままではクイズを1問答えたら終わりなので次の問題に回答できるようにします。

QuizController.phpのanswerアクションに記述します。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Question;

class QuizController extends Controller{
    public function create()
    {
        return view('quiz.create');
    }

    public function store(Request $request)
    {
        $question_table = new Question();

        $question_table->question = $request->question;

        $question_table->choices = $request->choices;

        $question_table->correct_choice = $request->correct_choice;

        $question_table->save();

        return back()->with('message', 'クイズを作成しました');
    }

    public function admin()
    {
        $quizs = Question::all();

        return view('quiz.admin', compact('quizs'));
    }

    public function delete(Question $question)
    {
        $question->delete();

        return back()->with('message', 'クイズを削除しました');
    }

    public function edit(Question $question)
    {
        return view('quiz.edit', compact('question'));
    }

    public function update(Request $request, Question $question)
    {
        $question->question = $request->question;

        $question->choices = $request->choices;

        $question->correct_choice = $request->correct_choice;

        $question->save();
        
        return back()->with('message', 'クイズを更新しました');
    }

    public function index()
    {
        $question = Question::first();

        return view('welcome', compact('question'));
    }

    public function show(Question $question)
    {
        return view('quiz.show', compact('question'));
    }

    public function answer(Question $question, Request $request)
    {
        $user_answer = $request->input('choice');

        $correct_choice_index = $question->correct_choice;

        $is_correct = false;

        if ($user_answer == $correct_choice_index) {
            $is_correct = true;
        }

        $next_question = Question::where('id', '>', $question->id)->first();       //この行を追加

        return view('quiz.answer', compact('question', 'user_answer', 'correct_choice_index', 'is_correct', 'next_question'));      //この行を修正
    }
}

「Question::where('id', '>', $question->id)->first()」で最初の問題のidの次のidに該当するレコードをquestionsテーブルから探しています。

answer.blade.phpのコードを下記にします。

<!DOCTYPE html><html lang="ja"><head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>回答</title></head><body>
    <div class="container">
        <h2>問題{{ $question->id }}の結果</h2>
        <p>あなたは選択肢{{ $user_answer }}を選びました。</p>
        @if ($is_correct)
            <p class="answer">正解です!</p>
        @else
            <p><span class="answer">不正解です。</span>正解は{{ $question->choices[$question->correct_choice - 1]}}です。</p>
        @endif


        //ここから追加
        @if ($next_question)
            <a href="{{ route('quiz.show', ['question' => $next_question->id]) }}">次の問題へ。</a>
        @else
            <p>クイズが終了しました。</p>
            <a href="/">トップページに戻る。</a>
        @endif
        //ここまで追加

        
    </div></body></html>

<style>
    .container{
        width: 70%;
        margin-left:auto;
        margin-right:auto;
    }
    .input{
        width: 60%;
        display: inline-block;
        margin-bottom: 20px;
    }
    .answer{
        font-weight: bold;
    }</style>

これでquestionsテーブルに次の問題のレコードがある場合は次の問題のリンクが表示されてない場合はクイズが終了になります。

今回はここまでです。

戻る