0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Laravel】morphToによるリレーションにおけるEager Loading

Posted at

前提

  • 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 ~の産物だということを始めて知りました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?