## 複数の値を一度に同じテーブルに入れる【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