Laravelでテーブルのリレーションを定義するhasManyとbelongsToの理解に時間がかかったので、整理がてらまとめてみました。
Contents
1対多(1対N)のリレーションとは?
「著者」と「本」の関係を例に見ていきましょう。
小説家・東野圭吾さんは、たくさんの本を書いています。
- 白夜行
- 容疑者Xの献身
- マスカレードホテル
- 夢幻花
・・・他多数
「1人の小説家が、多数の本を書いている」
これが「1対多」の関係です。
テーブルとサンプルデータの作成
2つのテーブルを作成します。
- 著者の情報を格納するテーブル:authors
- 本のタイトルを格納するテーブル:books
各テーブルのカラムとレコードを下記のように作ります。
テーブルとモデルのマッピング
「モデル」とは、テーブルの内容を定義したクラスです。モデルをテーブルに紐づけることで、テーブル操作が簡単にできます。
テーブルのカラム(フィールド)を、プロパティとして持たせているので、テーブルをインスタントして扱うことができます。
SQLをゴリゴリ書かなくても、レコードの抽出・挿入・更新・削除が簡単にできるのです。
1つのテーブルにつき、1つのモデルが紐づきます。Laravelにはテーブルとモデルを紐づける命名規則があります。
Laravelでは、「テーブル」と「モデル」を自動でマッピングするための命名規則があります。
モデル名=単数形/テーブル名=複数形で命名すると、自動でマッピングされます。
(例)
- モデル名=Book
- テーブル名=books
この命名規則を破ったネーミングにする場合、特別な記述をするとマッピングできます(この方法は当記事ではふれません)
たとえば、「people」という名前でテーブルを作成した場合、それに紐づけるモデル名は「Person」です。Personはpeopleの単数形ですからね!(むずかしい!)
Laravelの命名規則に従い、テーブルとモデルを作成します。
- authorsテーブル ⇔ Authorモデル
- booksテーブル ⇔ Bookモデル
リレーション元・リレーション先
テーブルにリレーションがある場合、リレーション元(主)・リレーション先(従)という考え方をします。
- リレーション元テーブル=authorsテーブル
- リレーション先テーブル=booksテーブル
これもLaravelの命名規則があります。
- リレーション元モデルのidカラム
- リレーション先モデルの、[リレーション元モデル名_id]カラム
この規則により、2つのテーブルはこのような関係になります。
リレーションを定義するメソッド
【1対多】のリレーションを定義するメソッドがあります。
- hasManyメソッド 【1対多】のリレーションを定義する
→【1】側で定義する(Authorモデル) - belongsToメソッド 【1対多】の逆向きのリレーションを定義する
→【多】側で定義する(Bookモデル)
hasManyのイメージ
“has”は、~を持っているという意味の動詞”have”の三人称単数形です。
池井戸潤さんも、本をたくさん書いていますね。同じく「1:多」の関係です。
belongsToのイメージ
【1対多】の関係を、【多】である「本」の立場から見てみましょう。
「本」は、かならず、ひとりの著者に所属します。
たとえば、『白夜行』というタイトルの本は、「東野圭吾」さんの本です。
『容疑者Xの献身』というタイトルの本も、「東野圭吾」さんの本です。
『七つの会議』というタイトルの本は、「池井戸潤」さんの本です。
これが【1対多】の「逆向きのリレーション」の考え方です。
モデルの作成
下記のコマンドでAuthorモデルとBookモデルを作成します。
1 2 3 4 |
$ php artisan make:model Author Model created successfully. $ php artisan make:model Book Model created successfully. |
app配下に、Author.phpとBook.phpが作成されます。
Authorモデル(hasMany結合)
hasManyメソッドを使用して、Bookモデルと【1対多】のリレーションを定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Author extends Model { public function books() { return $this->hasMany(Book::class); } } |
Bookモデル(belongsTo結合)
belongsToメソッドを使用して、Authorモデルと【1対多】の逆向きのリレーションを定義します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
<?php namespace App; use Illuminate\Database\Eloquent\Model; class Book extends Model { public function author() { return $this->belongsTo(Author::class); } } |
これで、AuthorモデルとBookモデルに「リレーション」ができました。
リレーションを使用したレコード取得
動作確認用にテーブルにデータを作成します。
1 2 3 4 5 6 7 8 |
mysql> select * from authors; +----+--------------+ | id | name | +----+--------------+ | 1 | 東野圭吾 | | 2 | 湊かなえ | | 3 | 池井戸潤 | +----+--------------+ |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
mysql> select * from books; +----+-----------+-----------------------------+ | id | author_id | book_title | +----+-----------+-----------------------------+ | 1 | 1 | 白夜行 | | 2 | 1 | 容疑者Xの献身 | | 3 | 3 | 空飛ぶタイヤ | | 4 | 2 | 告白 | | 5 | 3 | 下町ロケット | | 6 | 1 | マスカレードホテル | | 7 | 3 | 七つの会議 | | 8 | 2 | リバース | | 9 | 1 | 夢幻花 | | 10 | 2 | 豆の上で眠る | +----+-----------+-----------------------------+ |
tinkerを起動して名前空間を定義します。
1 2 3 |
$ php artisan tinker >>> use App\Book >>> use App\Author |
booksテーブル:id(7)の本の著者を取得する
booksテーブルのid=7は『七つの会議』です。『七つの会議』の著者は「池井戸潤」です。
1 2 |
>>> $author_name = Book::find(7)->author->name => "池井戸潤" |
想定どおり「池井戸潤」が取得できました。
コードを詳しくみてみましょう。
①Book::find(7)
Bookモデルはbooksテーブルに紐づいています。Bookモデルを通して、booksテーブルのid=7のレコードを取得します。
②Book::find(7)->author
Bookモデルに定義したauthorメソッドを呼び出します。
【1対多】の逆向きリレーション(belongsTo)で、authorsテーブルのレコードが取得できます。
※「authorsテーブルのid」と「booksテーブルのauthor_id」が紐づいていましたね
③Book::find(7)->author->name
Authorモデルのnameプロパティを取得します。これは、authorsテーブルのnameカラムです。
これが、リレーションを使用した値の取得方法です。
authorsテーブルのid=1は「東野圭吾」です。著者が「東野圭吾」の本をすべて取得します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
>>> $books = Author::find(1)->books => Illuminate\Database\Eloquent\Collection {#2843 all: [ App\Book {#2865 id: 1, author_id: 1, book_title: "白夜行", }, App\Book {#2868 id: 2, author_id: 1, book_title: "容疑者Xの献身", }, App\Book {#2869 id: 6, author_id: 1, book_title: "マスカレードホテル", }, App\Book {#2870 id: 9, author_id: 1, book_title: "夢幻花", }, ], } |
コードをみていきましょう。
①Author::find(1)
Authorモデルはauthorsテーブルと紐づいています。Authorモデルを通して、authorsテーブルのid=1のレコードを取得します。
②Author::find(1)->books
Authorモデルに定義したbooksメソッドを呼び出します。Authorに紐づくすべてのbookを取得します。
このように、テーブルリレーションを定義することで、テーブルのCRUD操作が簡単にできるのです。