【Laravel】リレーション
まずはDBの設計の話から、もう一度ちゃんと理解しよう
一対多
usersテーブルと、そのユーザー投稿を格納するpostsテーブルがあるとする。
usersひとりに対して紐づく投稿(post)は沢山あるので、usersからすると一対多の関係であると言える
usersから見れば、postsの中に紐づくレコードはたくさんある (一対多)
postsから見れば、usersの中に紐づくレコードはひとつしかない。(多対一)
⛳️親子関係について
相手のテーブルに対して複数の情報を持つものを親とする。
なのでこの場合はusersが親になる。
一対一
2つのテーブルの主キーが同じであったり、双方のレコードが一対一に対応する場合。
user_addressというユーザID, 住所, 郵便番号を持つテーブル
user_infoというユーザID, 読み仮名, 年齢などを持つテーブル
これらは一対一の関係。
ただし、一対一の関係は分割しなくても良いテーブルであることがほとんど。
多対多
片方のテーブルは、もう片方のテーブルと複数紐づく可能性がある。
またもう片方のテーブルから見ても、片方のテーブルと複数紐づく可能性があるもののこと。
RDBを使うならば、中間テーブルを作って対応すべき
中間テーブルを理解する
table:students
id name
1 山田
2 中島
3 小西
table:subjects
id subject_name
1 国語
2 算数
3 理科
students一人に対して、紐づくsubjectsは沢山ありそう
生徒は国語や算数など、いろいろな科目を履修する。
subjects一つに対しても、紐づくstudentsは沢山ありそう
国語は山田、中島、小西など、いろいろな生徒と紐づく。
これが多対多の関係。無理やりDBに格納しようとすると、紐づける分だけカラムが必要になる。
table:students
id name subject_1 subject_2 subject_3
1 山田 算数
2 中島 算数 理科 国語
3 小西 理科 国語
これを防ぐために中間テーブルを作成してあげる。履修テーブルを作ってみると
table:courses
id student_id subject_id
1 1 2
2 2 1
3 2 2
4 2 3
5 3 1
6 3 3
非常にはかどる
ブログの記事とカテゴリなんかも多対多になりがち。
記事Aは、日常や雑談やチラシ裏とか色々なカテゴリがつく可能性がある
同じくチラシ裏というカテゴリは、記事Aや記事Bや記事Cにつく可能性がある
---------------------------------------------------------------------------------------------------------------
Laravelのリレーション
リレーションを付与することで、クエリを簡易的に呼べるようになる
1対1
ModelにhasOneメソッドを書いて定義する。
code:User.php
// UserモデルにPhoneモデルの1対1のリレーションを書く
class User extends Model
{
public function phone() // モデルの単数形を書く。
{
return $this->hasOne('App\Phone');
}
}
Phoneモデルが、user_idという外部キーを持っている場合
code:php
// 第二引数に、その外部キーの値を入れる
// Userから見て、Phoneのuser_idとリレーションさせるというイメージ
return $this->hasOne('App\Phone', 'user_id');
🌟実はLaravelの機能で、自動でmodel_idとなっているカラムを探してくれる
まあ明示的にしておいた方がいいだろう
Phoneモデル側にbelongsToを記述する
今はUser > Phoneにアクセスできる状態。
今度はPhoneからUserにアクセスするような記述を書く。
code:php
class Phone extends Model
{
public function user()
{
return $this->belongsTo('App\User');
}
}
同じように、明示的に書くこともできる
code:php
// 外部キーは自分の持つキーを入れること。
// Phoneから見て、自分のuser_idとUserをリレーションさせるというイメージ
return $this->belongsTo('App\User', 'user_id');
1対多
親側(1の方)
code:php
class Post extends Model
{
public function comments()
{
// comments.post_idと1対多で紐付ける
return $this->hasMany('App\Comment');
}
}
子側(多の方)
1対1と同じく、belongsTo()て定義してやれば良い。
code:php
class Comment extends Model
{
public function post()
{
return $this->belongsTo('App\Post');
}
}
かあスレッドに書く
code:php
# User.php
public function post() {
return $this->hasMany('App\Models\Post', 'user_id');
}
# Post.php
public function user() {
return $this->belongsTo('App\User', 'user_id');
}
User側のTinkerテスト
code:php
$u = User::find(1);
=> App\User {#3315
id: 1,
name: "kirbis",
...
}
# 呼べるようになった
$u->post->count();
=> 697
Post側のTinkerテスト
code:php
$p = Post::find(1573);
=> App\Models\Post {#4000
id: 1573,
message: "AAA KAKI",...
}
# 誰がこの投稿を書いたのか、呼べるようになった
$p->user
=> App\User {#4947
id: 1,
name: "kirbis",...
}
$p->user->name
=> "kirbis"
おぉー
多対多
中間テーブルを持った構造を前提として話す。
テーブル1, 中間, テーブル2という構造であるが、1と2に記述する内容は同じ。
中間テーブルのModelには何も書かない。
ユーザーとロールの関係
code:php
class User extends Model {
public function roles() {
return $this->belongsToMany('App\Role');
}
}
class Role extends Model {
public function users() {
return $this->belongsToMany('App\User');
}
}
かあスレッドのは中間テーブルというかリアクションがおかしいから定義できない...
現状リアクションをviewで定義してるけど、DBで定義すれば楽そうだ...
→23.3.13直した。定義してみる
投稿とリアクションアイコンの関係が多対多になってそう
postには👀や👍など、reaction_icon_idが1や2のものが付く可能性がある
reaction_iconは、post_idが1や2のものにつく可能性がある
post
reactions←これが中間テーブルの役割を果たす。
reaction_icons
code:Post.php
// メソッド名は、複数形としてもOK
public function reaction_icons() {
// belongsToMany('最終的に繋げたいモデル','中間テーブル名','中間テーブルで使われているid1','id2')という感じ
return $this->belongsToMany(
'App\Models\ReactionIcon', 'reactions', 'post_id', 'reaction_icon_id'
);
}
これで、以下のような呼び出しができる
code:php
$p = App\Models\Post::find(1593);
// postに付与されているリアクションが一覧できる
$p->reaction_icons
=> Illuminate\Database\Eloquent\Collection {#4035
all: [
App\Models\ReactionIcon {#3315
id: 5,
name: "kaiddd_seijin",
...
// 中間テーブルの情報
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4247
post_id: 1593,
reaction_icon_id: 5,
},
},
App\Models\ReactionIcon {#4191
id: 11,
name: "mario_sad",
...
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#4037
post_id: 1593,
reaction_icon_id: 11,
},
},
App\Models\ReactionIcon {#4248
id: 10,
name: "thumbs_up",
...
pivot: Illuminate\Database\Eloquent\Relations\Pivot {#3998
post_id: 1593,
reaction_icon_id: 10,
},
},
],
}
リアクションアイコン側も同じ定義で良い。
code:ReactionIcon.php
public function posts() {
return $this->belongsToMany(
'App\Models\Post', 'reactions', 'reaction_icon_id', 'post_id'
);
}
// $r = \App\Models\ReactionIcon::find(11);
// $r->posts とすれば、mario_sadsがついている投稿を取得できる
中間テーブルのModelには特に定義しなくてOK
24.11.17
中間テーブルのレコードを更新する
updateExistingPivotメソッドを使用すれば良い。
その他もドキュメントを読むと良い。