実践知識

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

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

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


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

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

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

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

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

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

 
INDEX

 

1 対1の関係

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

本来なら親と子の関係はありませんが、Eloquentで記述する場合はどちらかを親と子(参照先テーブル)にしないといけません。

ここでは便宜上、参照先のモデル(テーブル)を「親」、参照するモデル(テーブル)を「子」と呼ぶとします。

 

具体例

親モデル:ユーザー(User)、子モデル:個人情報(Profile)

モデルに対するテーブルは以下のようにするとします。

 

User(親モデル)から Profile(子モデル)を引っ張ってくる

リレーションの定義


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

これだけでUserモデルはProfileモデルのデータを引っ張ることができます。

 
動作確認

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

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

このように profileメソッドをチェーンするだけで、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)から親モデル(User)を引っ張ってくる

今度は逆のケースです。

Profile から User へアクセスする定義を書いてみます。

リレーションの定義


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

 
動作確認

 

1 対 多 の関係

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

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

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

 

具体例

親モデル:ブログの投稿者(User)、子モデル:ブログの投稿(Post)

 

親モデル(User)から 子モデル(Post)を引っ張ってくる

書式ですが、基本的に 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()'()' がつくところもポイントです。

 

子モデル(Post)から 親モデル(User)を引っ張ってくる

リレーションの定義


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

 
動作確認

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

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

本庄マサノリ

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

 

-実践知識