Eloquentのリレーション(1対1、1対多)について調べてみた

Laravel/PHP
スポンサーリンク

はじめに

Laravelで開発中に、リレーションについて、曖昧な箇所や、なんとなく実装していた箇所ががあったため、調べ直してみました。1対1、1対多編!

開発状況(Version)

macOS Big Sur
Laravel 6系
PHP 7系

リレーションとは

Laravel ではモデルにリレーション=「関連」の設定ができるようになっています。
モデルにリレーションの設定をしておくと、簡単にリレーション先へアクセスができるようになります。

1対1の関係

1対1リレーションは、分割しなくてもよいテーブルが分割されている状態です。
Eloquentで記述する場合、どちらかを主テーブル、もう一方を従テーブルになります。

主テーブル:主キー(id)をもつテーブル
従テーブル:外部キー(〇〇_id)をもつテーブル

例えば

主テーブル(主キー):Usersテーブル

従テーブル(従キー):Videosテーブル

主テーブルから従テーブルのデータを引っ張ってくる方法

リレーションの定義

class User extends Authenticatable
{

---------抜粋----------- 

    //UserモデルがVideoモデルを所有している
  //1対1の場合、メソッドは単数になる
    public function video()
    {
        return $this->hasOne(Video::class);
    }

---------抜粋----------- 

}

※主テーブルが従テーブルをデータを引っ張ってくることができます。
hasOneとすると、オブジェクトが1個返ります。

動作確認

Tinkerを使用して、主テーブルが従テーブルを引っ張ってこれるか確認してみます。

[root@fcd514507bc6 html]# php artisan tinker
Psy Shell v0.9.12 (PHP 7.2.34 — cli) by Justin Hileman

User id:1のデータを表示

>>> App\User::find(1);
=> App\User {#3548
     id: 1,
     nickname: "信長のパパ",
     email: "oda@oda.com",
     delete_flag: "1",
     created_at: null,
     updated_at: null,
   }

UserテーブルからVideoテーブルの情報を取得表示

※User id:1のVideoテーブルでのデータが引っ張り出せている

>>> App\User::find(1)->video;
=> Illuminate\Database\Eloquent\Collection {#3229
     all: [
       App\Video {#3230
         id: 1,
         user_id: 1,
         url: "PkDfrVdCwCs",
         target_id: 1,
         created_at: "2022-04-03 12:38:03",
         updated_at: "2022-04-03 12:38:03",
       },
       App\Video {#4012
         id: 9,
         user_id: 1,
         url: "lZRNUvDOzmY",
         target_id: 1,
         created_at: "2022-04-03 12:38:03",
         updated_at: "2022-04-03 12:38:03",
       },
       App\Video {#4166
         id: 17,

このように、Videoメソッドをチェーンするだけで、従テーブルのデータを引っ張ってくることができます。

従テーブルから主テーブルのデータを引っ張ってくる方法

リレーションの定義

class Video extends Model
{

---------抜粋----------- 

    // Videoモデルが、Userモデルに所属している
    //1対1のメソッドは単数になる
    public function user()
    {
        return $this->belongsTo(User::class);
    }

---------抜粋----------- 

}

※従テーブルが主テーブルをデータを引っ張ってくることができます。

親モデルを呼び出すときは、「belongsTo」で定義します。

動作確認

Tinkerを使用して、従テーブルが主テーブルを引っ張ってこれるか確認してみます。

Video id:1のデータを表示

>>> App\Video::find(1);
=> App\Video {#4158
     id: 1,
     user_id: 1,
     url: "PkDfrVdCwCs",
     target_id: 1,
     created_at: "2022-04-03 12:38:03",

Videoテーブルから、Video id:1のデータに所持しているUserの情報を、Userテーブルから取得表示

>>> App\Video::find(1)->user;
=> App\User {#4012
     id: 1,
     nickname: "信長のパパ",
     email: "oda@oda.com",
     delete_flag: "1",
     created_at: null,
     updated_at: null,
   }

このように、Userメソッドをチェーンするだけで、主テーブルのデータを引っ張ってくることができます。

1対多の関係

Aテーブルのレコードは、Bテーブルの複数のレコードと関連する。
Bテーブルのレコードは、Aテーブルのレコードと最大1件のみ関連する。

1対多のリレーションは、親テーブル(1)に主キーを設けて、
それを参照する外部キーを子テーブル(多)に設ける

例えば

主テーブル(主キー):Userテーブル
従テーブル(外部キー):Videosテーブル

主テーブルから従テーブルのデータを引っ張ってくる方法

リレーションの定義

class User extends Authenticatable
{

---------抜粋----------- 

    //UserモデルがVideoモデルを所有している
  //1対多の場合、メソッドは複数形になる
    public function videos()
    {
        return $this->hasMany(Video::class);
    }

---------抜粋----------- 

}

※主テーブルが従テーブルをデータを引っ張ってくることができます。
hasManyとすると、コレクションで複数返ります。

動作確認

Tinkerを使用して、主テーブルが従テーブルを引っ張ってこれるか確認してみます。

User id:2のデータを表示

>>> App\User::find(2);
=> App\User {#4101
     id: 2,
     nickname: "秀吉のママ",
     email: "toyo@otoyo.com",
     delete_flag: "1",
     created_at: null,
     updated_at: null,
   }

Userテーブルから、User id:2に所持しているVideoのデータを、Videosテーブルから取得表示

>>> App\User::find(2)->videos;
=> Illuminate\Database\Eloquent\Collection {#3232
     all: [
       App\Video {#4167
         id: 2,
         user_id: 2,
         url: "ZSHhBm5OgrA",
         target_id: 3,
         created_at: "2022-04-03 12:38:03",
         updated_at: "2022-04-03 12:38:03",
       },
       App\Video {#3548
         id: 10,
         user_id: 2,
         url: "jZk_rwLgsC4",
         target_id: 3,
         created_at: "2022-04-03 12:38:03",
         updated_at: "2022-04-03 12:38:03",
       },
       App\Video {#4168
         id: 18,
         user_id: 2,
         url: "VvY9TEDtets",
         target_id: 3,
         created_at: "2022-04-03 12:38:03",
         updated_at: "2022-04-03 12:38:03",
       },
     ],
   }

クエリーに条件を付け加える

クエリに条件を付け加えて、チェーンで繋ぐこともできます。

User id :2が所持している、Videoのデータのうち、Videoテーブルのid:18のデータを取得表示

>>> App\User::find(2)->videos()->where('id', '18')->first();
=> App\Video {#4174
     id: 18,
     user_id: 2,
     url: "VvY9TEDtets",
     target_id: 3,
     created_at: "2022-04-03 12:38:03",
     updated_at: "2022-04-03 12:38:03",

※クエリビルダとしていろんな制約をするときは、最後にレコードを取得するメソッド(get()、first())が必要になります。

従テーブルから主テーブルのデータを引っ張ってくる方法

リレーションの定義

class Video extends Model
{

---------抜粋----------- 

    // Videoモデルが、Userモデルに所属している
    //1対多の1側のメソッドなので、単数になる
    public function user()
    {
        return $this->belongsTo(User::class);
    }

---------抜粋----------- 

}

※従テーブルが主テーブルのデータを引っ張ってくることができます。

こちらも「1対1」同様親モデルを呼び出すときは、「belongsTo」で定義します。

動作確認

Tinkerを使用して、従テーブルが主テーブルを引っ張ってこれるか確認してみます。

Videosテーブルに入っているデータで、Video id:2を所持しているUserデータを取得表示

>>> App\Video::find(2)->user;
=> App\User {#4174
     id: 2,
     nickname: "秀吉のママ",
     email: "toyo@otoyo.com",
     delete_flag: "1",
     created_at: null,
     updated_at: null,
   }

Videosテーブルに入っているデータで、Video id:2を所持しているUserデータのnicknameだけを取得表示

>>> App\Video::find(2)->user->nickname;
=> "秀吉のママ"

最後に

業務でリレーションを修正する実装があったので、復習を込めて、記事にしてみました。しかし、相変わらず、TinkerでDBを確認する際のコマンドがうる覚えでした。。。次は、多対多をまとめてみようかな。

参考記事

コメント