Eloquent のリレーション(1対1、1対多)

2019/07/24

Laravel ではモデルにリレーションの設定ができるようになっています。

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


$user = App\User::find(1);
foreach($user->roles as $role){
  // リレーション先のRoleモデルにアクセスできます
}

 
このように、わざわざINNER JOINで結合する必要はありません。

以前、Eloquent による基本的なリレーションについての記事をエントリーしました。

しかし、この記事では実際にどのカラムが紐づいているのかの説明が不足していました。

また、実践では紐づけるカラム名をこちら側で指定したいケースなど出てきます。

今回はその内容も踏まえてEloquentのリレーションについて再度エントリーします。

 
INDEX

 

1 対1の関係

1対1リレーションは、分割しなくてもよいテーブルが分割されている状態です。

Eloquent で記述する場合はどちらかを主テーブルにして、もう一方を従テーブルにしないといけません。

ポイント

主キー(id)を持つテーブルを主テーブル。
外部キー(***_id)を持つテーブルを従テーブル

 

具体例

主テーブル:ユーザー(users)、従テーブル:個人情報(profiles)

 

主テーブルから従テーブルを引っ張ってくる
リレーションの定義

# User.php
# -----------------------------
class User extends Model
{
  // 「1対1」→ メソッド名は単数形
  Public function profile()
  {
    // Profileモデルのデータを引っ張てくる
    return $this->hasOne('App\profile');
  }
}

 
これだけで主テーブルは従テーブルのデータを引っ張ることができます。

 

動作確認

ユーザーを作りTinkerで確認してみます。

Tinkerについてわからない方は以下をご参考ください。

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

ポイント
紐づけ条件について

内部ではどのようにテーブル間を紐づけしているのでしょうか?
実は Eloquentリレーションでは自動的に以下のカラムで紐づけします。

親モデルの id = 子モデルの「親モデル名_id」
→ users.id = profiles.user_id

Eloquent では 子モデル(profile)に対する外部キーを自動的に決めます。
規約ではモデル名に _id のサフィックスをつけた名前にします。
このケースの場合、Profile モデルは user_id というカラム名の外部キーを持っている必要があります。
つまり、profiles テーブルに user_id というカラムがなければ紐づけられません。
もし、user_id がいやなら、第2引数に別の外部キーのカラム名を指定してあげます。

子の外部キーを変更したい

:
Public function profile()
{
  // 「user_id」以外の外部キーにしたい
  return $this->hasOne('App\User', '子のモデルの外部キー');
}
:

 

親の id を別のカラム名に変えたい

Eloquent は User モデルの id カラムの値を Profile レコードの user_id カラムに存在しないか探します。「id」という名前がいやなら第3引数で親(User)のid扱いにするカラム名を指定してあげます。


:
Public function profile()
{
  //「user_id」以外の外部キーにしたい
  // さらにusers.id 以外のidカラムにしたい
  return $this->hasOne('App\User', '子のモデルの外部キー', '親のモデルの id キー');
}
:

 

従テーブルから主テーブルを引っ張ってくる

今度は逆のケースです。

従テーブルから主テーブルへアクセスする定義を書いてみます。

リレーションの定義

# Profile.php
# -----------------------------
class Profile extends Model
{
  // 「1対1」→ メソッド名は単数形
  Public function user()
  {
    return $this->belongsTo('App\User');
  }
}

 

動作確認

 

1 対 多 の関係

テーブルAのレコードはテーブルBの複数のレコードと関連する可能性があります。

しかしテーブルBのレコードはテーブルAのレコードと最大一件のみ関連します。

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

 

具体例

主テーブル:ブログの投稿者(users)、従テーブル:ブログの投稿(posts)

 

主テーブルから従テーブルを引っ張ってくる

書式ですが、基本的に hasOne と同じです。

ポイント

「1対1」と「1対多」はあまり大きな違いはありません。オブジェクトが一個返るか、コレクションで複数返るかの違いしかないです。
ちなみに参照先の親モデルを呼び出すときも「belongsTo」で定義します。

リレーションの定義

# User.php
# -----------------------------
class User extends Model
{
  // 「1対多」の「多」側 → メソッド名は複数形
  public function posts()
  {
    return $this->hasMany('App\Post');
  }
}

 

動作確認

中身が欲しい場合は、foreachでループをまわしてあげます。


$posts = User::find(1)->posts
Foreach($posts as $post){}

 

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

クエリに条件をつけてチェーンでつなぐことができます。


App\User::find(8)->posts()->where('title','test')->first();

ポイント

クエリビルダとしていろんな制約をするときは、最後にレコードを取得するメソッド( get()、first() )が必要になります。
また、今度は posts()'()' がつくところもポイントです。

 

従テーブルから主テーブルを引っ張ってくる
リレーションの定義

# Profile.php
# -----------------------------
class Post extends Model
{
// ユーザーが投稿した記事を取得
// 1対 多 の1側なので単数形
public function user()
{
return $this->belongsTo('App\User');
}
}

ポイント

従テーブルには「1対1」「1対多」関わらず belongsTo() メソッドを定義します。

 

動作確認

 
Laravel のデータベースのリレーションの解説は PHPフレームワーク Laravel入門 が詳しく記述されています。

理解までもう一歩!のかたは是非読んでみてください。

 
「多 対 多」については少し考え方が違うので、次回にエントリーしようと思います。

とりあえず「1対1」と「1 対 多」については以上になります。

本庄マサノリ

仕事で Laravel を使っています。気づいたことや新しい発見など情報を発信していきます。問い合わせはこちら

>> Twitter をフォローする

 

-実践知識