Laravelのみでクイズアプリを作る方法④クイズ問題を編集・削除する方法

Laravelのみでクイズアプリを作る方法④クイズ問題を編集・削除する方法

143 回閲覧されました

みなさんこんにちは、joinioです。

クイズ問題を編集する方法はtodoアプリみたいなアプリと違い少しやり方が違うのでやり方を作成します。

一応削除機能も載せますがこれは簡単だと思うのでコードのみを掲載します。

記事を読むに当たって

今回の解説の記事を書くにあたり先にアプリを作っていますが1回目の解説と今回の解説では期間がかなり空いています。

だからビューの見た目をかなり変えたり新しいモデル・ビュー・コントローラーを作成していますのでご自身でビューは補ってください。

先に編集・削除のページのルーティング・コントローラー・ビューを開設します。

管理画面のルーティング・コントローラー・ビュー

web.phpを下記にします。

Route::controller(AdminController::class)->group(function(){
    Route::get('/admin/index', 'index')->name('admin.index');
});

AdminController.phpを作成して下記の記述にします。

<?php

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

class AdminController extends Controller
{
    public function index(){
        $questions = Question::all();

        return view('admin.index', compact('questions'));
    }
}

index.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">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css">
    <link href="{{ asset('css/admin.css') }}" rel="stylesheet" type="text/css">
    <link href="{{ asset('css/reset.css') }}" rel="stylesheet" type="text/css">
    <meta name=”robots” content=”noindex”/>
    <title>3択クイズ</title>
</head>
<body>
    <div class="question-container ml-auto mr-auto pt-6 pb-6">
        @if(session('message'))
            <p class="is-block message has-text-centered pt-2 pb-2">{{ session('message') }}</p>
        @endif
        <h2 class="has-text-centered has-text-weight-bold is-size-4">管理画面</h2>
        <h2 class="mt-6 has-text-centered has-text-weight-bold is-size-4">問題一覧</h2>
        <img class="mt-4 is-block ml-auto mr-auto" src="{{ asset('images/questions.png') }}" alt="問題一覧">
        <table border="1" class="mt-6">
            <tr>
                <th class="has-text-centered pt-4 pb-4">問題文</th>
                <th class="has-text-centered pt-4 pb-4">選択肢</th>
                <th class="has-text-centered pt-4 pb-4">正解</th>
                <th class="has-text-centered pt-4 pb-4">管理</th>
            </tr>
            @foreach($questions as $question)
            <tr>
                <td class="pl-3 pr-3 pt-4 pb-4 question">{{ $question->question }}</td>
                <td class="pl-3 pr-3 pt-4 pb-4">
                    @foreach($question->choices as $index => $choice)
                    <p class="choice">選択肢{{ $index + 1 }} : {{ $choice }}</p>
                    @endforeach
                </td>
                <td class="pl-3 pr-3 pt-4 pb-4">
                    <p class="choice">{{ $question->correct_choice }}</p>
                </td>
                <td class="pl-3 pr-3 pt-4 pb-4 admin">
                    <a href="{{ route('admin.edit', ['id' => $question->id]) }}" class="link">編集</a>
                    <form class="is-inline-block" method="post" action="">
                        <button id="delete" class="deleteButton button">削除</button>
                    </form>
                </td>
            </tr>
            @endforeach
        </table>
        <h2 class="mt-6 has-text-centered has-text-weight-bold is-size-4">その他の管理</h2>
        <div class="columns mt-4">
            <a href="{{ route('quiz.create') }}" class="back column  has-text-centered" >
                <img class="image mx-auto" src="{{ asset('images/create.png') }}" alt="問題を作る">
                <p class="has-text-centered mt-2">問題を作成する</p>
            </a>
            <a href="/" class="back column" >
                <img class="image mx-auto" src="{{ asset('images/home.png') }}" alt="トップページ">
                <p class="has-text-centered mt-2">トップページに戻る</p>
            </a>
        </div>
    </div>
</body>
</html>

これで「URL/admin/index」にアクセスすると下記の表示になります。

それでは本題の編集機能の解説に入ります。

ルーティング(編集機能ページの表示)

web.phpを下記にします。

Route::controller(AdminController::class)->group(function(){
  Route::get('/admin/{id}/edit', 'edit')->name('admin.edit');
});

コントローラー(編集機能ページの表示)

AdminController.phpを下記にします。

<?php

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

class AdminController extends Controller
{
    public function index(){
        $questions = Question::all();

        return view('admin.index', compact('questions'));
    }


    //ここから追加
    public function edit($id){
        $choiceId = Question::find($id);

        return view('admin.edit', compact('choiceId'));
    }
    //ここまで追加
    

}

次はビューです。

ビュー(編集機能ページの表示)

edit.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">
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.9.3/css/bulma.min.css">
    <link href="{{ asset('css/admin.css') }}" rel="stylesheet" type="text/css">
    <link href="{{ asset('css/reset.css') }}" rel="stylesheet" type="text/css">
    <meta name=”robots” content=”noindex”/>
    <title>問題の編集</title>
</head>
<body>
    <div class="question-container ml-auto mr-auto pt-6 pb-6">
        @if(session('message'))
            <p class="is-block message has-text-centered pt-2 pb-2">{{ session('message') }}</p>
        @endif
        <h2 class="has-text-centered has-text-weight-bold is-size-4">問題の編集</h2>
        <form class="mt-6" action="{{ route('admin.update', ['question' => $choiceId]) }}" method="post">
            @csrf
            @method('put')
            <div class="mt-3">
                <label class="label">問題文 : </label>
                <input type="text" name="question" class="input" value="{{ old('question', $choiceId->question) }}">
            </div>
            <div class="mt-3">
                <label class="label">選択肢1 : </label>
                <input type="text" name="choices[0]" class="input" value="{{ old('choices[0]', $choiceId->choices[0]) }}">
            </div>
            <div class="mt-3">
                <label class="label">選択肢2 : </label>
                <input type="text" name="choices[1]" class="input" value="{{ old('choices[1]', $choiceId->choices[1]) }}">
            </div>
            <div class="mt-3">
                <label class="label">選択肢3 : </label>
                <input type="text" name="choices[2]" class="input" value="{{ old('choices[2]', $choiceId->choices[2]) }}">
            </div>
            <div class="mt-3">
                <label class="label">正解の選択肢</label>
                <select name="correct_choice" class="select" value="{{ old('correct_choice', $choiceId->correct_choice) }}">
                    <option value="1">選択肢1</option>
                    <option value="2">選択肢2</option>
                    <option value="3">選択肢3</option>
                </select>
            </div>
            <input type="submit" value="問題を更新する" class="is-block mt-2" >
        </form>
        <a href="{{ route('admin.index') }}" class="is-block mt-5" >管理画面に戻る</a>
    </div>
</body>
</html>

これで編集画面が下記になります。

問題の編集は問題を作成する時とコードが大体同じになるのですが選択肢のデータ型が「json」になると違う部分が発生します。(問題の選択肢の型を忘れた人はマイグレーションファイルを確認すれば分かります)

問題を作成するビューのcreate.blade.phpは下記です。(bodyタグの中のみ掲載します)

<body>
    <div class="question-container ml-auto mr-auto pt-6 pb-6"">
        @if(session('message'))
            <p class="is-block message has-text-centered pt-2 pb-2">{{ session('message') }}</p>
        @endif
        <h2 class="has-text-centered has-text-weight-bold is-size-4">問題の作成</h2>
        <form class="mt-6" action="{{ route('quiz.store') }}" method="post">
            @csrf
            <div class="mt-3">
                <label class="label">問題文 : </label>
                <input type="text" name="question" class="input" value="人気があるプログラミング言語は?">
            </div>
            <div class="mt-3">
                <label class="label">選択肢1 : </label>
                <input type="text" name="choices[]" class="input" value="HTML">
            </div>
            <div class="mt-3">
                <label class="label">選択肢2 : </label>
                <input type="text" name="choices[]" class="input" value="React">
            </div>
            <div class="mt-3">
                <label class="label">選択肢3 : </label>
                <input type="text" name="choices[]" class="input" value="COBOL">
            </div>
            <div class="mt-3">
                <label class="label">正解の選択肢</label>
                <select name="correct_choice" class="select">
                    <option value="1">選択肢1</option>
                    <option value="2">選択肢2</option>
                    <option value="3">選択肢3</option>
                </select>
            </div>
            <input type="submit" value="登録する" class="is-block mt-2" >
        </form>
        <a href="{{ route('admin.index') }}" class="is-block mt-5 back" >管理画面に戻る</a>
    </div>
</body>

選択肢のname属性が明らかに違いますよね。

name="choices[]"            //create.blade.php
name="choices[0]"           //edit.blade.php

問題を作成する時は問題のカラムに値を入れるのでchoicesのうしろに「[ ]」が付きます(インデックス番号の指定は必要ない)が問題を編集する時は編集する問題文を指定しないといけないのでchoicesのうしろの「[ ]」の中にインデックス番号を入れないといけません。

それでは問題を更新させます。

ルーティング(問題の更新)

web.phpに追記します。

Route::controller(AdminController::class)->group(function(){
    Route::put('/admin/{question}/update', 'update')->name('admin.update');
});

モデル(問題の更新)

Question.phpを下記にします。

<?php

namespace App\Models;

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

class Question extends Model
{
    use HasFactory;

    protected $fillable = [
        'question',
        'choices',
        'correct_choice'
    ];

    protected $casts = [
        'choices' => 'array'
    ];

    function quizStore($request){
        $inputs = $request->validate([
            'question' => 'required',
            'choices' => 'required',
            'correct_choice' => 'required'
        ]);

        $this->question = $inputs['question'];
        $this->choices = $inputs['choices'];
        $this->correct_choice = $inputs['correct_choice'];
        $this->save();
    }

    function quizDelete($request){
        $this->delete();
    }
}

コントローラー(問題の更新)

AdminController.phpに追記します。

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

  return back()->with('message', 'クイズの編集が完了しました');
}

これで問題の更新ができます。

ポイントはビュー(edit.blade.php)のname属性の書き方でした。

最後に問題を削除する方法のコードを掲載します。

問題の削除

web.phpに追記します。

Route::controller(AdminController::class)->group(function(){
    Route::delete('/admin/delete/{question}', 'delete')->name('admin.delete');
});

Question.phpに追記します。

function quizDelete($request){
  $this->delete();
}

AdminController.phpに追記します。

public function delete(Question $question, Request $request){
  $question->quizDelete($request);

  return back()->with('message', '問題の削除をしました');
}

index.blade.phpに追記します。(bodyタグの中のみ記載します)

<body>
    <div class="question-container ml-auto mr-auto pt-6 pb-6">
        @if(session('message'))
            <p class="is-block message has-text-centered pt-2 pb-2">{{ session('message') }}</p>
        @endif
        <h2 class="has-text-centered has-text-weight-bold is-size-4">管理画面</h2>
        <h2 class="mt-6 has-text-centered has-text-weight-bold is-size-4">問題一覧</h2>
        <img class="mt-4 is-block ml-auto mr-auto" src="{{ asset('images/questions.png') }}" alt="問題一覧">
        <table border="1" class="mt-6">
            <tr>
                <th class="has-text-centered pt-4 pb-4">問題文</th>
                <th class="has-text-centered pt-4 pb-4">選択肢</th>
                <th class="has-text-centered pt-4 pb-4">正解</th>
                <th class="has-text-centered pt-4 pb-4">管理</th>
            </tr>
            @foreach($questions as $question)
            <tr>
                <td class="pl-3 pr-3 pt-4 pb-4 question">{{ $question->question }}</td>
                <td class="pl-3 pr-3 pt-4 pb-4">
                    @foreach($question->choices as $index => $choice)
                    <p class="choice">選択肢{{ $index + 1 }} : {{ $choice }}</p>
                    @endforeach
                </td>
                <td class="pl-3 pr-3 pt-4 pb-4">
                    <p class="choice">{{ $question->correct_choice }}</p>
                </td>
                <td class="pl-3 pr-3 pt-4 pb-4 admin">
                    <a href="{{ route('admin.edit', ['id' => $question->id]) }}" class="link">編集</a>
                    <form class="is-inline-block" method="post" action="{{ route('admin.delete', ['question' => $question]) }}">         //action属性に追記
                        @csrf                            //この行を追加
                        @method('DELETE')                //この行を追加
                        <button id="delete" class="deleteButton button">削除</button>
                    </form>
                </td>
            </tr>
            @endforeach
        </table>
        <h2 class="mt-6 has-text-centered has-text-weight-bold is-size-4">その他の管理</h2>
        <div class="columns mt-4">
            <a href="{{ route('quiz.create') }}" class="back column  has-text-centered" >
                <img class="image mx-auto" src="{{ asset('images/create.png') }}" alt="問題を作る">
                <p class="has-text-centered mt-2">問題を作成する</p>
            </a>
            <a href="/" class="back column" >
                <img class="image mx-auto" src="{{ asset('images/home.png') }}" alt="トップページ">
                <p class="has-text-centered mt-2">トップページに戻る</p>
            </a>
        </div>
    </div>
</body>

これで削除機能も動作します。