複数の値を一度に同じテーブルに入れる【Laravel】


複数の値を一度に同じテーブルに入れる【Laravel】

アンケートなどを作成するとき、一つの質問に対して、答えを複数用意したい。 そんな時のためのメモ。

環境:Laravel 8.50.0

まずはQuestionモデルとテーブル、Answerモデルとテーブルを作成。

Questionモデル

namespace App\Models;

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

class Question extends Model
{
    use HasFactory;
    protected $guarded = [];

    public function answers()
    {
        return $this->hasMany(Answer::class);
    }
}


questionsテーブル

    public function up()
    {
        Schema::create('questions', function (Blueprint $table) {
            $table->id();
            $table->string('question');
            $table->timestamps();
        });
    }


Answer モデル

<?php

namespace App\Models;

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

class Answer extends Model
{
    use HasFactory;
    protected $guarded = [];

    public function question()
    {
        return $this->belongsTo(Question::class);
    }
}


answersテーブル

    public function up()
    {
        Schema::create('answers', function (Blueprint $table) {
            $table->id();
            $table->foreignId('question_id')->constrained()->cascadeOnDelete();
            $table->string('answer');
            $table->timestamps();
        });
    }


web.php

Route::get('/questionnaires/{questionnaire}/question/create', [QuestionController::class, 'create'])->name('question.create');
Route::post('/questionnaires/{questionnaire}/questions', [QuestionController::class, 'store'])->name('question.store');
Route::get('/questionnaires/{questionnaire}', [QuestionnaireController::class, 'show'])->name('questionnaire.show');


question/create.blade

@extends('layouts.app')

@section('content')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8">
                <div class="card">
                    <div class="card-header">Create New Question</div>

                    <div class="card-body">
                        <form action="{{ route('question.store', $questionnaire->id) }}" method="post">

                            @csrf

                            <div class="form-group">
                                <label for="question">Question</label>
                                <input name="question[question]" type="text" class="form-control"
                                    value="{{ old('question.question') }}" id="question" aria-describedby="questionHelp"
                                    placeholder="Enter Question">
                                <small id="questionHelp" class="form-text text-muted">Ask simple and to-the-point questions
                                    for best results.</small>

                                @error('question.question')
                                    <small class="text-danger">{{ $message }}</small>
                                @enderror
                            </div>

                            <div class="form-group">
                                <fieldset>
                                    <legend>Choices</legend>
                                    <small id="choicesHelp" class="form-text text-muted">Give choices that give you the most
                                        insight into your question.</small>

                                    <div>
                                        <div class="form-group">
                                            <label for="answer1">Choice 1</label>
                                            <input name="answers[][answer]" type="text"
                                                value="{{ old('answers.0.answer') }}" class="form-control" id="answer1"
                                                aria-describedby="choicesHelp" placeholder="Enter Choice 1">

                                            @error('answers.0.answer')
                                                <small class="text-danger">{{ $message }}</small>
                                            @enderror
                                        </div>
                                    </div>

                                    <div>
                                        <div class="form-group">
                                            <label for="answer2">Choice 2</label>
                                            <input name="answers[][answer]" type="text"
                                                value="{{ old('answers.1.answer') }}" class="form-control" id="answer2"
                                                aria-describedby="choicesHelp" placeholder="Enter Choice 2">

                                            @error('answers.1.answer')
                                                <small class="text-danger">{{ $message }}</small>
                                            @enderror
                                        </div>
                                    </div>

                                    <div>
                                        <div class="form-group">
                                            <label for="answer3">Choice 3</label>
                                            <input name="answers[][answer]" type="text"
                                                value="{{ old('answers.2.answer') }}" class="form-control" id="answer3"
                                                aria-describedby="choicesHelp" placeholder="Enter Choice 3">

                                            @error('answers.2.answer')
                                                <small class="text-danger">{{ $message }}</small>
                                            @enderror
                                        </div>
                                    </div>

                                    <div>
                                        <div class="form-group">
                                            <label for="answer4">Choice 4</label>
                                            <input name="answers[][answer]" type="text"
                                                value="{{ old('answers.3.answer') }}" class="form-control" id="answer4"
                                                aria-describedby="choicesHelp" placeholder="Enter Choice 4">

                                            @error('answers.3.answer')
                                                <small class="text-danger">{{ $message }}</small>
                                            @enderror
                                        </div>
                                    </div>

                                </fieldset>
                            </div>

                            <button type="submit" class="btn btn-primary">Add Question</button>

                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
@endsection


細かく見てみます。

question

<div class="form-group">
<label for="question">Question</label>
//'question.question'で受け取る
<input name="question[question]" type="text" class="form-control"
value="{{ old('question.question') }}" id="question" aria-describedby="questionHelp"
placeholder="Enter Question">
<small id="questionHelp" class="form-text text-muted">Ask simple and to-the-point questions
for best results.</small>

@error('question.question')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>


answer1

<div>
<div class="form-group">
<label for="answer1">Choice 1</label>
//answeresを配列で渡した1番目のanswer。answerがkey。
<input name="answers[][answer]" type="text"
//1番目なので0を指定 
value="{{ old('answers.0.answer') }}" class="form-control" id="answer1"
aria-describedby="choicesHelp" placeholder="Enter Choice 1">

@error('answers.0.answer')
<small class="text-danger">{{ $message }}</small>
@enderror
</div>
</div>

answer2,3,4も同じ。


QuestionController

class QuestionController extends Controller
{
    public function create(Questionnaire $questionnaire)
    {
       return view('question.create',compact('questionnaire'));
    }

    public function store(Questionnaire $questionnaire)
    {
        //Validationして$dataに入れる。
        $data = request()->validate([

            //question['question']
            'question.question' =>' required',
            //arrayを入力するのでワイルドカード
            'answers.*.answer' =>' required',
        ]);
          
        //$data['question']を作成
        $question = $questionnaire->questions()->create($data['question']);
        //$data['answers']を作成 answersは複数あるのでcreateMany
        $question->answers()->createMany($data['answers']);

        return redirect()->route('questionnaire.show',[$questionnaire->id]);
    }
}


QuestionnaireController

    public function show(Questionnaire $questionnaire)
    {
        //lazy loading.questionsと一緒にquestionsとリレーションを貼ったanswersを呼び出す。取得できる結果はwith(Eager)と同じ
        $questionnaire->load('questions.answers'); 
            
        return view('questionnaire.show',compact('questionnaire'));
    }


questionnaire/show.blade

//受け取った変数を$questionとしてループ
@foreach ($questionnaire->questions as $question)
  <div class="card mt-4">
    <div class="card-header">{{ $question->question }}</div>

  <div class="card-body">
     <ul class="list-group">
   //$questionとのリレーション$answersを$answerとしてループ
    @foreach ($question->answers as $answer)
       <li class="list-group-item">{{ $answer->answer }}</li>
    @endforeach
     </ul>
    </div>
  </div>
@endforeach

Question Answer1 Answer2 Answer3 Answer4

と出力されます。



参考 https://youtu.be/_SyG3HMv48k