前提
- Documentを見てて、「こんなのあるんだ!」と知ったのでサクッとアウトプット
- 私の実務経験歴:1か月
- 基本的なLaravelの使い方は割愛
開発環境
- Windows 10 Home
- Laravel 8.x
- PHP 7.x
やりたいこと
- 以下二つのリレーションを設定する
- Order→User→UserAttribute
- Order→Guestuser
- ↓こんな感じのeager loadingが確認できることが目標
ユースケース
- 「登録ユーザー」(User)と「ゲストユーザー」(GuestUser)が共存するECサイト
- 同じ画面で同じ項目(例えば、年齢(age)など)を取得し、出力する
参考
Laravel公式ドキュメント「morphToリレーションのネストしたEagerロード」
https://readouble.com/laravel/8.x/ja/eloquent-relationships.html#nested-eager-loading-morphto-relationships
前提
モデル構成
- 必要最低限だけ記入
Order Model
- テーブル名:orders
カラム名 | 型 | 備考 |
---|---|---|
id | int | PK |
orderable_id | int | FK |
name | string |
User
- テーブル名:users
カラム名 | 型 | 備考 |
---|---|---|
id | int | PK |
name | string |
GuestUser
- テーブル名:guest_users
カラム名 | 型 | 備考 |
---|---|---|
id | int | PK |
name | string | |
age | string |
UserAttribute
- テーブル名:user_attributes
カラム名 | 型 | 備考 |
---|---|---|
id | int | PK |
user_id | int | FK |
age | string |
Route
- なんでもいいので、GETメソッドのルートを決めます。
Route::get('/hello',[HelloController::class, 'index']);
Controller
- 最低限のuseしかしていないです。
<?php
namespace App\Http\Controllers;
use ...
use Illuminate\Database\Eloquent\Relations\MorphTo;
use App\Models\Order;
class HelloController extends Controller
{
public function index(Request $request)
{
$orders = Order::query()
->with(['orderable' => function (MorphTo $morphTo) {
$morphTo->morphWith([
User::class => ['userAttribute'],
]);
}])->get();
}
View
- dump結果だけなので、割愛
Model
Order
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Order extends Model
{
use HasFactory;
// UserとGuestUserのポリモーフィック
public function orderable(){
return $this->morphTo();
}
}
User
class User extends Authenticatable
{
...
// UserAttributeの値を取得
public function userAttribute(){
return $this->hasOne(UserAttribute::class);
}
}
GuestUser, UserAttribute
特に記載する必要なし
動作確認
-
http://localhost/プロジェクト名(/public)/hello
にアクセスしデバッグバーでクエリを確認- クエリが
select * from 'user_attribute' where 'user_attribute'.'user_id' = in (1, 2, 3, ...);
とin句を使って取得できていれば成功 - クエリが以下のようにin句ナシで取得できている場合はEagerloadingができておらず、N+1問題が発生している
select * from 'user_attribute' where 'user_attribute'.'user_id' = 1;
select * from 'user_attribute' where 'user_attribute'.'user_id' = 2;
select * from 'user_attribute' where 'user_attribute'.'user_id' = 3;
- クエリが
ちなみに…
- 最初僕が試したのは以下参考に示したネストしたEager Loading
- Controllerのwithメソッドをこうしてみた
$orders = Order::with('orderable.userAttribute');
- 「めっちゃシンプルにEager Loadingできんじゃん!」とテンション上がりましたが、
Undefined method 'userAttribute' at [App\Models\GuestUser]
※訳「GuestユーザーにuserAttribute
なんてねーよ!バカ!」
と怒られました。
GuestUserにはuserAttributeなんてメソッドは存在しない。そもそも必要ないのだ。浅はかでした。
参考
morphToのリレーションにおけるEagerloadingはLaravel 5.8 ~の産物だということを始めて知りました。