LaravelのEloquentでリレーションを作る方法!(hasOne, hasMany)

Laravel Laravel

LaravelのEloqunetでリレーションを作る方法をまとめてみました。
Laravelのバージョン8で挙動を確認しています。

モデルから関連するモデルにリレーションを作成しておくと、簡単に関連先のデータを取得することが可能です。
複雑な条件などが不要な場合は、クエリビルダーでJoinを使わずにこちらで取得すると良いかと思います。

公式のこちらの内容をわかりやすくしてみました。
今回はこちらの中から、hasOneとhasManyをピックアップして記事にしています。

hasOne, hasMany検証用のテーブル構成

今回リレーションを試してみるために作成したテーブル構成は下記です。
掲示板のアプリケーションを考えたときのテーブルになります。
Boardsが掲示板で、Messagesが返信です。

Boardsモデル(テーブル名:boards)

項目名
id bigint(20)
title varchar(255)
user_name varchar(255)
content varchar(255)

Messagesモデル(テーブル名:messages)

項目名
id bigint(20)
board_id bigint(20)
user_name varchar(255)
message varchar(255)

1対1でリレーションする(hasOneを使用する)

モデルにhasOneを書くことで、引数で渡したクラスのモデルを1つだけ返してくれます。

class Board extends Model
{
    // --- 省略 ---
    public function message()
    {
        return $this->hasOne(Message::class);
    }
}

引数で渡したモデルには、呼び出し元のidを持っている必要があります。
今回の例で言うと、board_idmessagesが持っているため取得できます。

呼び出し元のモデルのidカラムと呼び出し先の[呼び出し元のテーブル名 + ‘_id’]カラムがリレーションで使うカラムとして自動的に選択されます。

hasOneで、ひとつのデータを取得する

リレーションを作った先のモデルを取得する時は、親のモデルをひとつ取得し、そこからメソッドを呼びます。
下記のように書くことで、BoardからMessageをひとつ取得します。

Board::find(1)->message

ddで確認すると、このように表示されます。
関連するmessagesテーブルのモデルが1件返ってきているのが確認できました。

App\Models\Message {#1010 ▼
  #fillable: array:3 [▶]
  #connection: "mysql"
  #table: "messages"
  #primaryKey: "id"
  #keyType: "int"
----- 省略 -----
}

リレーション先のデータが存在しない場合は結果がnullなります。

1対Nでリレーションする(hasManyを使用する)

モデルにhasManyを書くことで、引数で渡したクラスのモデルを複数返してくれます。

class Board extends Model
{
    // --- 省略 ---
    public function messages()
    {
        return $this->hasMany(Message::class);
    }
}

hasOneと同じで、messagesがboard_idを持っているため、このように書くことが可能です。

hasManyで、複数のデータを取得する

使用するときは、下記のように呼び出すと複数取得することができます。

Board::find(1)->messages

取得した結果を出力すると、下記のようにコレクションの中にモデルが複数入って返ってきていることが確認できます。

Illuminate\Database\Eloquent\Collection {#1010 ▼
  #items: array:2 [▼
    0 => App\Models\Message {#1011 ▶}
    1 => App\Models\Message {#1220 ▶}
  ]
}

hasOne, hasManyでキーが違う場合はどうする?

Eloquentでは、呼び出し元のテーブル名に基づいて、キーが決定します。

今回の例で言うとBoardモデルからMessageを呼び出しているので、messagesテーブルにはboard_idが必要ということです。
Eloquentはboard_idと呼び出し元のboardsテーブルのidを結合して取得してきます。

このデフォルトのキーがリレーションしたいキーではない場合は、モデルを呼び出す時に引数として渡すことができます。

phonesテーブルとusersテーブルがあるとします。

Usersモデル(テーブル名:users)

項目名
id bigint(20)
user_number bigint(20)
name string
email string

Phonesモデル(テーブル名:phones)

項目名
id bigint(20)
user_number bigint(20)
phone_number string

phonesテーブルにはuser_idの代わりに、user_numberがあるとします。
Userモデルから呼び出す場合は、下記のようにリレーションするときのキーを指定することができます。

public function phone()
{
    return $this->hasOne(Phone::class, 'user_number');
}

こうすると、呼び出し先のphonesテーブルのuser_numberと呼び出し元のusersテーブルのidでリレーションが作れます。

もし、usersテーブルのリレーションしたいキーがidじゃない場合は、下記のように渡すことができます。
こうすると、呼び出し先のphonesテーブルのuser_numberと呼び出し元のusersテーブルのuser_numberをリレーションすることになります。

public function phone()
{
    return $this->hasOne(Phone::class, 'user_number', 'user_number');
}

ここではhasOneを例にしていますが、hasManyの場合も同じように渡すことができます。

つまりhasOneの場合は下記のようになり…

$this->hasOne(Phone::class, '呼び出し先のキー', '呼び出し元のキー');

hasManyの場合も同じですが、下記のようになります。

$this->hasMany(Phone::class, '呼び出し先のキー', '呼び出し元のキー');

おわりに

今回はEloquentのリレーションについて記載しました。

簡単にリレーションを作ることが可能なことがわかったかと思います。
気をつけるべきは、呼び出し先のテーブルに[呼び出し元のテーブル名+’_id’]で作っておかないといけませんでした。
ない場合は取得することができないので、作成するようにしてください。

また、リレーションしたいカラムが別のカラムの場合でも引数に指定することでリレーションが作れることもわかりました。
複雑な条件などがない場合はリレーションで取得して使うと良いかと思います。

コメント

タイトルとURLをコピーしました