【Laravel】1対多リレーションを便利に使うEloquent
LravelでEloquentを使って1対多のリレーションを確立していろいろ操作してみます。
今回は生徒のデータを入れる「students」テーブルと宿題のレポートを集めた「repors」テーブルを用意します。
一人の生徒は複数のレポートを提出できるので1対多の関係になります。
手順はざっくりとこんな感じです。
- studentsテーブルのマイグレーションファイルを作りマイグレーションする(特に特殊な設定は必要なし)
- reportsテーブルのマイグレーションを作りマイグレーションする(student_idカラムを持たせる)
まず、studentsテーブルのマイグレーションファイルは特に変わったところはなしです。
カラムは「id」「name」「created_at」「updated_at」
create_students_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateStudentsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('reports', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('students'); } }
続けて宿題を保存する「reports」テーブルのマイグレーションファイル。
特筆すべきは「student_id」カラムを持たせることです。
主となるテーブル(今回の場合students)の名前の単数形_idという形で従テーブル(今回はreportsテーブル)にカラムを持たせると、Laravelが自動的に判断して便利にリレーションしてくれます。
create_reports_table.php<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Database\Schema\Blueprint; use Illuminate\Support\Facades\Schema; class CreateReportsTable extends Migration { /** * Run the migrations. * * @return void */ public function up() { Schema::create('reports', function (Blueprint $table) { $table->id(); $table->integer('student_id'); //(リレーションを持たせる主テーブルの単数形_id)の形でカラム名を定義するとlaravelがリレーションを確立してくれる $table->string('title'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('reports'); } }
Aくんの情報とAくんが提出したレポートの情報を同時に取得する
とある生徒Aくんの情報をAくんが提出したレポートの一覧と共に取得するにはStudentモデルをカスタマイズすると便利です。
Studentモデルにreports()メソッドを追加してみましょう。
app/Models/Student.php<?php namespace App\Models; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; class Student extends Model { use HasFactory; // ここから下です。 public function reports() { return $this->hasMany('App\Models\Report'); } }
hasMany()メソッドを持たせることで、Studentの情報(studentsテーブルのid)に紐づくreportsテーブルの情報も一緒に取ってくることができます。
hasMany()だけではなくhasOne()、belongsTo()、belongsToMany()などいろいろあるのですが、今回の場合は「A君は複数のレポートを提出できる = Studentモデルは複数のReportモデルを持つ = Student model has many report models」という関係になるのでhasMany()メソッドです。
public function reports() {
return $this->hasMany('App\Models\Report');
}
repors()メソッドを作成してその中でhasMany()メソッドを使っているので使用する時はreportsを使います。
このメソッドを作ることによりコントローラ等から以下の用に実行すると、Aくんが提出したレポート全てが抽出できるようになります。
ちなみにいろいろ試してみたけどメソッド名(今回の場合はreports)は違う名前にしても動きました。
Student::find(1)->reports;
全生徒のデータをレポート情報込で取得する
では次に全生徒のデータを、各生徒に紐付いているレポート情報付きで取得してみましょう。
その場合も先程Studentモデルに定義した「reports()」メソッドを使うと便利です。
生徒一覧 + 各生徒が提出したレポート一覧です。
Student::with('reports')->get();
これでOKです。簡単です。
レポートを提出した生徒一覧を取得する
先程は全生徒でしたが今度はレポートを提出した生徒のみの一覧を取り出してみます。
レポートを提出した生徒一覧です。
Student::whereHas('reports')->get();
レポートを提出した生徒一覧をレポート情報付きで取得する
どんどん行きましょう。
先程はレポートを提出した生徒一覧でしたが、レポートを提出した生徒をレポート情報付きで取得してみましょう。
ややこしくなってきた(笑)
レポートを提出した生徒一覧各+生徒が提出したレポート一覧です。
Student::whereHas('reports')->with('reports')->get();
です!
この「whereHas()」メソッドですが、リレーション相手のテーブルにて検索できるので例えばこんな具合にすると「1月1日以降にレポートを提出した生徒一覧をレポート情報付きで出力する」なんてこともできます。
1月1日以降にレポートを提出した生徒一覧各+生徒が提出したレポート一覧です。
Student::whereHas('reports',
function($query) {
$query->where('created_at', '>', '2020-01-01 00:00:00');
}
)->with('reports')->get();
さらに「whereHas()」メソッドに引数を持たせたい場合は「use()」メソッドをつければOKです。
use($変数)という感じです。
$my_time = '2020-01-01 00:00:00';
Student::whereHas('reports',
function($query) use ($my_time) {
$query->where('created_at', '>', $my_time);
}
)->with('reports')->get();