21
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Laravel Eloquent リレーションは怖くない

Last updated at Posted at 2020-12-02

はじめに

以前に書いた記事がいくつか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 を使います。

App\Models\User.php
use App\Models\UserAge;

public function age() {
    return $this->hasOne(UserAge::class);
}

子(user_idを持つ)から親(idを持つ)を参照するには、 belongsTo を使います。

App\Models\UserAge.php
use App\Models\User;

public function user() {
    return $this->belongsTo(User::class);
}

準備しておけば下記のように記述できます。

.php
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 を使います。

App\Models\User.php
use App\Models\UserBook;

public function books() {
    return $this->hasMany(UserBook::class);
}

子(user_idを持つ)から親(idを持つ)を参照するには、先ほどと同じく belongsTo を使います。

App\Models\UserBook.php
use App\Models\User;

public function user() {
    return $this->belongsTo(User::class);
}

準備しておけば下記のように記述できます。

.php
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 を使用します。

App\Models\User.php
use App\Models\Book;

public function books() {
    return $this->belongsToMany(Book::class);
}

books テーブルから book_user という中間テーブルを経由して users データを取得する場合も、 belongsToMany を使用します。

App\Models\UserBook.php
use App\Models\User;

public function users() {
    return $this->belongsToMany(User::class);
}

準備しておけば下記のように記述できます。

.php
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 メソッドを使えば追加できます。

.php
$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 オブジェクトのメソッドが利用できます。

.php
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

新しくレコードを追加したい場合は、下記のように実装します。

.php
$user->books()->create(['name' => '週刊少年ジャンプ+']);

Illuminate\Database\Eloquent\Relations\BelongsToMany

新しく本を紐付けしたい場合は下記のように実装します。

.php
$user->books()->attach([1, 2, 3]); // books テーブルのID

// 新しく books.id が 1, 2, 3 のものが追加されます。

また、本の紐付けを同期したい場合は下記のように実装します。

.php
$user->books()->sync([1, 2, 3]); // books テーブルのID

// books.id が 1, 2, 3 のものが追加され、それ以外のidは削除されます。

最後に

社内用です。
ソースを書きながら記事を書いたわけではないので、
ところどころ動かしてみると間違ってる可能性があるので、
その場合は都度修正します…。

21
15
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
15

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?