はじめに
以前に書いた記事がいくつかLGMTいただいていたので、
こちらに現在自分の知識での最善のリレーションを記述しておきます。
親子が1対1の関係
テーブル構成
[親] users テーブル
色々な子テーブルを持っています。
もちろん、次に登場する user_ages テーブルもです。
| id | name |
|---|---|
| 1 | 田中太郎 |
| 2 | 山田花子 |
| 3 | 佐藤一郎 |
[子] user_ages テーブル
ユーザーの年齢を管理するテーブル。
users テーブルとのレコードは1対1の関係。
| id | user_id | age |
|---|---|---|
| 1 | 1 | 23 |
| 2 | 2 | 34 |
| 3 | 3 | 55 |
Model側の実装
親(idを持つ)から子(user_idを持つ)を参照するには、 hasOne を使います。
use App\Models\UserAge;
public function age() {
return $this->hasOne(UserAge::class);
}
子(user_idを持つ)から親(idを持つ)を参照するには、 belongsTo を使います。
use App\Models\User;
public function user() {
return $this->belongsTo(User::class);
}
準備しておけば下記のように記述できます。
use App\Models\{ User, UserAge };
$user = User::find(1);
echo $user->age->age; // 23
$age = UserAge::find(3);
echo $age->user->name; // 佐藤一郎
親子が1対多の関係
テーブル構成
[親] users テーブル
再登場した users テーブル。
次は1ユーザーが複数の 本 を持つことができる、 user_books テーブルを参照します。
| id | name |
|---|---|
| 1 | 田中太郎 |
| 2 | 山田花子 |
| 3 | 佐藤一郎 |
[子] user_books テーブル
ユーザーの持っている本のタイトルを管理するテーブルです。
エルマーのぼうけん シリーズ が人気のようです。
同じ user_id のレコードが複数行できる可能性があります。
| id | user_id | name |
|---|---|---|
| 1 | 1 | エルマーのぼうけん |
| 2 | 1 | 週刊少年ジャンプ+ |
| 3 | 3 | エルマーのぼうけん |
| 4 | 3 | エルマーとりゅう |
| 5 | 3 | エルマーと16ぴきのりゅう |
Model側の実装
親(idを持つ)から複数の子(user_idを持つ)を参照するには、 hasMany を使います。
use App\Models\UserBook;
public function books() {
return $this->hasMany(UserBook::class);
}
子(user_idを持つ)から親(idを持つ)を参照するには、先ほどと同じく belongsTo を使います。
use App\Models\User;
public function user() {
return $this->belongsTo(User::class);
}
準備しておけば下記のように記述できます。
use App\Models\{ User, UserBook };
// 田中太郎(ID:1)さんの持つ本の一覧を表示してみましょう。
$user = User::find(1);
foreach ($user->books as $book) {
echo $book->name . PHP_EOL;
}
// エルマーのぼうけん
// 週刊少年ジャンプ+
// エルマーとりゅう(ID:4)は誰の持ち物か表示してみましょう。
$book = UserBook::find(4);
echo $book->user->name; // 佐藤一郎
多対多の関係
テーブル構成
users テーブル
先程は「親子」と言っていましたが、多対多の関係では親子の縁は切れました。
なぜなら、どちらも親になる可能性があるからです。
| id | name |
|---|---|
| 1 | 田中太郎 |
| 2 | 山田花子 |
| 3 | 佐藤一郎 |
books テーブル
本のタイトルをそのまま持つと、本のタイトルが変わったときに一気にレコードを更新しないといけません。
毎回、複数のレコードを更新するのはめんどくさいですよね?
なら、本 は books テーブルで管理しましょう。
| id | name |
|---|---|
| 1 | 週刊少年ジャンプ+ |
| 2 | おそ松さん |
| 3 | エルマーのぼうけん |
| 4 | エルマーとりゅう |
| 5 | エルマーと16ぴきのりゅう |
book_user テーブル
ユーザー と 本 を結びつける中間テーブルです。
なぜ book_user[s] じゃないのかって?
Laravelでは中間テーブルの名前は[複数形にしないルール](### 基本的なLaravelにおけるテーブルの命名規則)になっています。
ちなみに、 users テーブルと books テーブルの中間テーブル名は、
アルファベットの早い方が先頭になります。
したがって、 book_user テーブルとなります。
| id | user_id | book_id |
|---|---|---|
| 1 | 1 | 3 |
| 2 | 1 | 1 |
| 3 | 3 | 3 |
| 4 | 3 | 4 |
| 5 | 3 | 5 |
Model側の実装
users テーブルから book_user という中間テーブルを経由して books データを取得する場合は、 belongsToMany を使用します。
use App\Models\Book;
public function books() {
return $this->belongsToMany(Book::class);
}
books テーブルから book_user という中間テーブルを経由して users データを取得する場合も、 belongsToMany を使用します。
use App\Models\User;
public function users() {
return $this->belongsToMany(User::class);
}
準備しておけば下記のように記述できます。
use App\Models\{ User, Book };
// 佐藤一郎(3)さんの持つ本の一覧を表示してみましょう。
$user = User::find(3);
foreach ($user->books as $book) {
echo $book->name . PHP_EOL;
}
// エルマーのぼうけん
// エルマーとりゅう
// エルマーと16ぴきのりゅう
// エルマーのぼうけん(3)を持っている人一覧を表示してみましょう。
$book = Book::find(3);
foreach ($book->users as $user) {
echo $user->name . PHP_EOL;
}
// 田中太郎
// 佐藤一郎
また、 山田花子 さんに おそ松さん を持たせたい場合は、
下記のように attach メソッドを使えば追加できます。
$user = User::find(2); // 山田花子
$user->books()->attach(2); // おそ松さん(2)を追加します。
その他、覚えておくと良いこと
基本的なLaravelにおけるテーブルの命名規則
LaravelでDBを使用する場合のテーブル名は、マスタ・データ問わず基本的に 複数形 になります。
また、中間テーブルのみ 単数形 となります。
$user->age と $user->age() の違い
$user->age はSQLを実行して、その結果を App\Models\UserAge で返します。
$user->age() はSQLが実行されず、Illuminate\Database\Eloquent\Relations\HasOne のようなリレーションオブジェクトを返します。
下記のように、続けて HasOne オブジェクトのメソッドが利用できます。
if ($user->age()->exists()) {
// \App\Models\UserAge モデルが存在する
} else {
// \App\Models\UserAge モデルが存在しない
}
$user->books()
$user->books()は、
1対多の関係の場合は Illuminate\Database\Eloquent\Relations\HasMany
多対多の関係の場合は Illuminate\Database\Eloquent\Relations\BelongsToMany を返します。
Illuminate\Database\Eloquent\Relations\HasMany
新しくレコードを追加したい場合は、下記のように実装します。
$user->books()->create(['name' => '週刊少年ジャンプ+']);
Illuminate\Database\Eloquent\Relations\BelongsToMany
新しく本を紐付けしたい場合は下記のように実装します。
$user->books()->attach([1, 2, 3]); // books テーブルのID
// 新しく books.id が 1, 2, 3 のものが追加されます。
また、本の紐付けを同期したい場合は下記のように実装します。
$user->books()->sync([1, 2, 3]); // books テーブルのID
// books.id が 1, 2, 3 のものが追加され、それ以外のidは削除されます。
最後に
社内用です。
ソースを書きながら記事を書いたわけではないので、
ところどころ動かしてみると間違ってる可能性があるので、
その場合は都度修正します…。