トップページ
Laravel学習サイトLaravelやるばい

目次

ランキング表

今回がクイズアプリ作成の最後の記事になります。

人によって難しく感じるかもしれないので難しいと思ったらこの記事は読まない方がいいです。

今回はクイズを全て答えたあとのランキング表です。

ランキング表用のテーブルのscoresテーブルを作成します。


scoresテーブルの作成

下記のコマンドを叩きます。

php artisan make:model Score -m

マイグレーションファイルに記述する前にカラムは何が必要かを考えます。

必要になる情報は下記だと思います。

正解数のカラムをscore・ユーザーはuser_idとします。

user_idということはリレーションを使うということです。

1人のユーザーに対して複数の正解数を紐づけることができるのでusersテーブルが親テーブルでscoresテーブルが子テーブルと見ることができます。

仕事ではリレーションが当たり前に登場するので使い方を覚えた方がいいです。

マイグレーションファイルに下記の記述をします。

<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('scores', function (Blueprint $table) {
            $table->id();


            //ここから追加
            $table->unsignedBigInteger('user_id');
            $table->integer('score');
            //ここまで追加


            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('scores');
    }
};

テーブルを作成したらScoreモデルにカラムの値を登録できるようにする為の記述をします。


正解数をscoresテーブルに保存する

Scoreモデルに下記の記述をします。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Score extends Model
{
    use HasFactory;

    protected $fillable = [
        'score',
        'user_id'
    ];
}

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

<?php

namespace App\Http\Controllers;

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


//ここから追加
use App\Models\Score;
use Illuminate\Support\Facades\Session;
//ここまで追加


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;


        //ここから追加
        $questions = Question::count();

        Session::increment('next_question_number');
        //ここまで追加


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

            Session::increment('correct_number');         //この行を追加
        }

        $next_question = Question::where('id', '>', $question->id)->first();


        //ここから追加
        $next_question_number = Session::get('next_question_number');

        $correct_number = Session::get('correct_number', 0);

        if ($next_question_number == $questions) {
            $score_record = new Score([
                'user_id' => auth()->id(),
                'score' => $correct_number
            ]);

            $score_record->save();

            Session::forget('next_question_number');

            Session::forget('correct_number');
        }
        //ここまで追加
        

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

今回はセッションという機能を使います。

通常ページからページに変数を持ち越すことができません。

そこで変数を保持したままにするセッションを使います。

不思議な感じがするかもしれませんがセッションは身近な所で使っています。

それはログインです。

ログインは何をしているかというとセッションを使ってユーザーの情報をページをまたいでも保持できるようにしています。

それではコードの解説をします。

「Question::count();」はquestionsテーブルのレコードの数を数えています。

クイズの回答数がquestionsテーブルのレコードの数と同じになった時の判定条件に使います。

「Session::increment('next_question_number')」は変数名をnext_question_numberとしてクイズに回答するたびに値が1ずつ増えて値は保持される意味です。

これは何問目のクイズに答えているかに使っています。

「Session::get('next_question_number')」はnext_questoin_numberの値を取得するのに使います。

これにより回答したクイズの数とquestionsテーブルのレコード数と同じかの判定ができるようになります。

「Session::get('correct_number', 0)」は「, 0」がありますがこれはcorrect_numberの値がなかった場合は0を使うという意味です。

correct_numberの値がないのは1問もクイズに正解できない場合です。

if文はクイズに答える回数がquestionsテーブルのレコード数と同じになったらscoresテーブルに情報を保存するという意味です。

「Session::forget('next_question_number')」はセッション情報を削除するという意味になります。

この記述がないとクイズの回答が全て終わってscoresテーブルに情報を保存したあとにnext_questoin_numberの値が削除されず増え続けるのでもう一度全てのクイズに答えてもscoresテーブルに情報を保存することができなくなります。

万が一実装を間違えてエラーが出た場合はクイズに全て答えたあとにnext_questoin_numberの値が増え続けてscoresテーブルに情報が保存されなくなります。

アプリを一旦ログアウトすればセッション情報が削除されるのでログアウトして下さい。

これで正解数をscoresテーブルに保存できるようにしたのでランキング表を作成します。


ランキング表の作成

welcome.blade.phpに記述しますが変数を渡せるようにする為にQuizController.phpに記述しますがその前に考えることがあるので解説します。

scoresテーブルにuser_idカラムがありますがidの値なので今のままでは直接ユーザー名を表示できません。

しかしリレーションを使えばuser_idカラムの値に該当するuersテーブルのnameカラムの値を表示できます。

子テーブルの情報から親テーブルの情報を引っ張ってくる・親テーブルの情報から子テーブルの情報を引っ張ってくることができますが今回はscoresテーブルのuser_idカラムからusersテーブルのnameカラムの値を引っ張ってくるので子テーブルから親テーブルの情報を引っ張ってきます。

引っ張ってくる元のモデルに記述をしないといけないです。

だからScoreモデルに記述します。

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Score extends Model
{
    use HasFactory;

    protected $fillable = [
        'score',
        'user_id'
    ];


    //ここから追加
    public function user()
    {
        return $this->belongsTo(User::class);
    }
    //ここまで追加

    
}

関数名に関して単数系・複数形のルールがあって下記になります。

メソッドの中身の書き方も決まっています。

子テーブルの場合は下記です。

return $this->belongsTo(親モデル名::class);

親テーブルの場合は下記です。

return $this->hasMany(子モデル名::class);

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

それではQuizController.phpの記述に戻ります。

下記の記述をします。

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\Question;
use App\Models\Score;
use Illuminate\Support\Facades\Session;

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();

        $user_scores = Score::orderBy('score', 'DESC')->get();       //この行を追加

        return view('welcome', compact('question', 'user_scores'));       //この行を修正
    }

    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;

        $questions = Question::count();

        Session::increment('next_question_number');

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

            Session::increment('correct_number');
        }

        $next_question = Question::where('id', '>', $question->id)->first();

        $next_question_number = Session::get('next_question_number');

        $correct_number = Session::get('correct_number');

        if ($next_question_number == $questions) {
            $score_record = new Score([
                'user_id' => auth()->id(),
                'score' => $correct_number
            ]);

            $score_record->save();

            Session::forget('next_question_number');

            Session::forget('correct_number');
        }

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

「::orderBy('score', 'DESC')」の意味はscoreカラムの値が大きい順に並べるという意味です。

最後にテンプレートのコードを下記にします。

<!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 -->
       
    </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 class="ranking">
                    <h2>ランキング表</h2>
                    <table border="1">
                        <tr>
                            <th>ユーザー名</th>
                            <th>正解数</th>
                        </tr>
                        @foreach ($user_scores as $user_score)
                            <tr>
                                <td>{{ $user_score->user->name }}</td>
                                <td>{{ $user_score->score }}</td>
                            </tr>
                        @endforeach
                    </table>
                </div>
                //ここまで追加


            </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;
    }
    .ranking{
        margin-top: 50px;
    }
    

    //ここから追加
    table, td, th{
        border:1px solid black;
        border-collapse: collapse;
        width:fit-content;
        margin-left:auto;
        margin-right:auto;
    }
    td, th{
        padding: 10px;
        text-align: center;
    }
    //ここまで追加
</style>

ビューでリレーションを使って値を表示しているのが下記のコードです。

{{ $user_score->user->name }}

「user」はScoreモデルに記述した関数名です。

userを経由してusersテーブルのカラムにアクセスできるようになります。

「->name」はusersテーブルのnameカラムです。

これでクイズに回答すると下記の表示のランキング表が表示れます。

20250101_113608_quiz15.jpg

これでクイズアプリは完成です。




戻る