Laravelの1対1リレーションのhasOneについて、公式マニュアルでは専用単語ばかりでどうしてもわかりにくいと感じてしまっている方へ向けて、わかりやすく解説しました。
※例で紹介しているコードについては、一部英語を日本語表記で使用している部分もありますので、コピペで使用する方は必要に応じて置き換えてください。
今回は以下のLaravel7系の公式マニュアルを参考にしながらまとめています。
子から親へのリレーション(belongsTo)について詳しく知りたい方は、以下をご参考ください。
hasOne(親から子へ)
# 公式より
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* ユーザーに関連する電話レコードを取得
*/
public function phone()
{
return $this->hasOne('App\Phone');
}
}
1対1のリレーションとは、とあるテーブルの1つの要素と別のテーブルの1つの要素の関係を結びつけることです。この時、どちらかが複数になることはありません。
そのため、イレギュラーがなければ各テーブルの行数は以下の関係が成り立ちます。
親 ≧ 子
親のいない子が生まれるということは有りえませんので当然のことです。
※道徳的な意味は除きます
ここで重要になるのは、テーブル関係のどちらが親で、どちらが子に該当するかです。これはテーブル内の値の持ち方によって決められます。
公式マニュアル通り、ユーザーと電話のテーブルで考えてみましょう。
上記テーブル関係の場合、ユーザーが親で電話が子になります。この見分け方は、判別するためのIDを持っているテーブルが子になるということです。電話テーブルにはユーザーを判別するための「ユーザー_id」がありますね。
判別するIDを持っている方が子になる
親と子の見分け方がわかったので、次にhasOneについて見ていきましょう。
公式マニュアルのコードをわかりやすく上記テーブルのように日本語へ置き換えて考えます。
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class ユーザー extends Model
{
public function 電話()
{
return $this->hasOne('App\電話);
}
}
hasOneは親から子に結びつけるためのリレーションなので、ユーザーのモデルに記述します。この時、電話のモデルが存在しなければリレーションができないので、事前に準備しておきましょう。
引数の解説
hasOne('モデル', '外部キー[省略可]', 'ローカルキー[省略可]');
hasOneでは上記のように第3引数まで設定でき、第2、3引数は省略することが可能です。それでは1つずつ順番に確認していきましょう。
第1引数
モデルには、リレーション先(子)のモデルを指定します。今回であれば、親であるユーザーテーブルに「子の電話テーブルと結びつける」という指示を出したいので、第一引数には子に該当する電話モデルを指定します。
return $this->hasOne('App\電話');
Laravelのテーブル命名規則に従っていれば、これだけでリレーション関係を結びつけることができます。
リレーションで役立つLaravelのテーブル命名規則をざっくり解説
-
親テーブル側の判別カラムが「id」
※カスタムプライマリーキーでも可 -
子テーブル側の判別カラムが「親テーブル単数名 + _id」
今回は上記の条件が満たされているので、第2、第3引数は省略可能です。
第2引数
第2引数では、外部キーを指定することができます。外部キーという表現がわかりにくい場合は「親を判別するための値が格納されている、子テーブルのカラム名」と考えてください。
hasOneの外部キーとは
親を判別するための値が格納されている、子テーブルのカラム名
今回のテーブル関係であれば親は「ユーザー」、子が「電話」ですね。電話が持つユーザーを判別する値は「ユーザー_id」なので、省略した場合は第2引数にはこの値が初期値としてセットされることになります。
return $this->hasOne('App\電話', 'ユーザー_id');
例えば、テーブルのカラムが以下の用に設定されていたケースで考えてみましょう。
これだと第1引数でモデルを指定しても、命名規則に従っていなければどのユーザーのidと電話のどの値が一致する行と結びつければいいかわかりません。上記テーブルであれば、親であるユーザーを判別している値が格納されているリレーション先のカラムは「ユーザー番号」なので、以下のようにリレーションを指定します。
return $this->hasOne('App\電話', 'ユーザー番号');
こうすることで、リレーション先のカラム名に関係なくリレーション関係を結ぶことができます。
第3引数
第3引数ではローカルキーを指定することができます。こちらもわかりやすく言い換えれば、「親を判別する値が格納された親テーブルが持つカラム」です。
hasOneのローカルキーとは
親を判別する値が格納された親テーブルが持つカラム
以下のテーブルで考えてみましょう。
※idの値を使用して判別するのが一般的で、このような格納方法をしているケースはまずありませんが、hasOneをわかりやすく理解するために上記テーブル関係を使用します
第2引数では子(電話)が持つ親(ユーザー)を判別する値が格納されたカラムを指定するので「ユーザーの名前」が入ります。そして、第3引数では親(ユーザー)を判別する値が格納された親テーブルが持つカラムを指定するので「名前」を指定します。
return $this->hasOne('App\電話', 'ユーザーの名前', '名前');
これでリレーションを結ぶことができました。第3引数までを上手く利用すれば、1対1の関係性が成立していればリレーションを結べます。
条件の追加
モデルにリレーションを追加する際に、様々な条件に合わせて取得する値を変えたり、不要なデータはそもそも取り除いて取得したいというケースもありますね。そういったときにはリレーションに対して条件をつけることができます。そのいくつかをご紹介します。
論理削除を含む
もしsoft delete(論理削除)を使用している場合は、通常通りリレーションを結べばdeleted_atがセットされているデータは紐付けされません。しかし、場合によっては必要になるケースもあります。その使用頻度が高いのであれば、リレーションとして準備しておくことで開発がよりスムーズになります。
論理削除したデータも含めたい場合は、以下のように記述します。
return $this->hasOne('App\Phone')->withTrashed();
withTrashedというメソッドをつなげることで、論理削除した値も含めて取得してくれます。
条件に合わせた絞り込み
他にも、条件に応じて絞り込みをしたいケースもあるでしょう。その時は、Laravelのwhereメソッドなどを利用できます。
以下のテーブルを参考に、statusが1の値だけをリレーションで取得します。
return $this->hasOne('App\Phone')->where('status', '1');
これでstatusの値が1になっている番号だけを紐付けすることができます。リレーション段階で安易につなげてしまえば、必要なデータが取得できないようなことにもなりますので、必要に応じてモデルのメソッドを増設するなどして対応しましょう。
まとめ
いかがだったでしょうか。
今回はLaravelの1対1リレーション「hasOneについて」をわかりやすく解説しました。
公式マニュアルを見ても専用単語ばかりで分かりづらいと感じて曖昧なままリレーションを使っていたり、断念してしまった方はぜひ参考にしてくださいね。
子から親へのリレーション(belongsTo)について詳しく知りたい方は、以下をご参考ください。
Laravelの基礎を学習したい方は、以下の書籍が参考になります。