Eloquentリレーション入門
大抵のテーブルは他のテーブルに関連づいている。ユーザー情報一つにしても、個人情報から、クレカ情報など様々なテーブルのデータで形成されている。
Eloquentはそうしたリレーションを簡単に管理し操作できるようにしている。
リレーションの仕方
モデル・テーブル作成
php artisan make:model Profile -m
Profileモデルとそれに紐付いたmigrationファイルが作られる。
##1対1の関係
###HasOne
1対1の関係(ユーザーと個人情報など)
ユーザーに紐付いた個人情報のリレーションを定義
Public function profile()
{
return $this->hasOne(Profile::class);
}
ユーザーを作りTinkerで確認する
$user = User::find(1)->profile;
=> App\Profile {#2876
id: 1,
user_id: 1,
github_url: "",
twitter_url: "",
website_url: "sauna-hacker.com",
created_at: null,
updated_at: null,
実際どのようなSQLが発行できたか見てみる。
DB::enableQueryLog()でクエリのログを記録することができる
DB::getQueryLog()でそれを出力。
>>> DB::getQueryLog();
=> [
[
"query" => "select * from `users` limit 1",
"bindings" => [],
"time" => 0.6,
],
[
"query" => "select * from `profiles` where `profiles`.`user_id` = ? and `profiles`.`user_id` is not null limit 1",
"bindings" => [
1,
],
"time" => 0.4,
],
]
User_idをもとにデータを取得しているのがわかる。
では、User_id以外のカラムと紐づけたい場合はどうするのか!?
デフォルトだとUser_idを見てしまうので、この規約をオーバーライドしてあげます。
第2引数をもたせます
Public function profile()
{
return $this->hasOne(Profile:classs, 'foreign_key');
}
逆にUser.idではないカラムで紐づけたい場合は
第3引数をもたせる
Public function profile()
{
return $this->hasOne(Profile:classs, 'foreign_key', 'hoge');
}
belongsTo
上記ではUserからProfileにはアクセスすることができた。
次は、逆にProfileからUserへとアクセスする方法を定義する
Public function user()
{
return $this->belongsTo(User::class);
}
hasOneのときとほぼ同じである。
しかし、Eloqunetでは
リレーションメソッド名 + _idのサフィックスをつけたものをデフォルトの外部キーにしているため、
外部キーが異なる場合はメソッドの第2引数にカスタムキー名を渡す必要がある。
Public function user()
{
return $this->belongsTo(User::class, 'foreign_key');
}
親キーの主キーがIDではない、もしくは子のモデルとは異なるカラムで紐づけたい場合は第3引数を渡す。
Public function user()
{
return $this->belongsTo(User::class, 'foreign_key', 'other_id');
}
##1対多の場合
Public function posts()
{
return $this->hasMany(Post::class)
}
メソッド名が複数形になるのがポイント
Eloquentのコレクションという形でアクセスできる。
Illuminate\Database\Eloquent\Collection {#2906
all: [
App\Post {#2889
id: 1,
user_id: 1,
title: "hoge",
body: "hogehoge",
created_at: "2018-09-17 18:23:07",
updated_at: "2018-09-17 18:23:01",
},
App\Post {#2898
id: 2,
user_id: 1,
title: "hoge",
body: "hogehoge",
created_at: "2018-09-17 18:23:07",
updated_at: "2018-09-17 18:23:01",
},
],
}
中身が欲しい場合は、Foreachでループをまわしてあげる。
$posts = User::find(1)->posts
Foreach($posts as $post){}
すべてのリレーションはクエリビルダとしても働くため、クエリに条件をつけてチェーンでつなぐことができる。
$posts = User::find(1)->posts()->where('title', 'hoge')->first()
ここでのポイントはposts()に'()'がつくこと。
HasOneと同様に、外部キーとローカルキーを与える引数に渡すことでオーバーライドできる
Return $this->hasMany(Post::class, 'post_id', 'id');
##1対多(Inverse)
先程は、1→多だったが今度はその逆、多→1である。
Postに対して多くのCommentが紐付いており、そのCommentからPostを取得する場合
Public function post()
{
Return $this->belongsTo(Post::class);
}
1対1の時と同様。
リレーションが定義できたらPostの動的プロパティでPostモデルを取得する
$comment = Comment::find(1);
$comment->post->title;
多対多 の関係
ユーザーと役目の関係
ユーザーが多くの役目をもっている
役目も多くのユーザーに共有されている
Public function roles()
{
return $this->belognsToMany(Role::class);
}
return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id')
- 第二引数: 結合テーブル名
- 第三引数: リレーションを定義しているモデルの外部キー名
- 第四引数: 結合するモデルの外部キー名
中間テーブルのカラム取得
多対多リレーションには中間テーブルが必要
モデルのPivot属性を使うことにより中間テーブルにアクセスできる
$user = User::find(1);
Foreach ($user->role as $role)
{
Echo $role->pivot->created_at
}
中間テーブルのcreated_at,updated_atタイムスタンプを自動的に保守したい場合はwithTimestampメソッドをリレーション定義
Return $this->belongsToMany('App\Role')->withTimestamps
Pivot属性の名前を変更することができる
Return $this->belongsToMany('App\Product')
->as('subscription')
->withTimestamp();
中間テーブルを利用した関係のフィルタリング
Return $this->belongsToMany('App\Role')->wherePivot('approved', 1);
Has Many Thorough
仲介するテーブルをとおして、直接関連づいていないテーブルへアクセスできる。
Countries
Id integer
Name string
Users
Id integer
country_id integer
Name string
Posts
Id integer
user_id integer
Title string
上記のようなテーブル構成の時に、CountriesとPostsは直接紐付いていない、
しかし、Usersテーブルを介してだと追うことができる。
Public function posts()
{
Return $this->hasManyThrough('App\Post', 'App\User');
}
- 第一引数: 最終的にアクセスしたいモデル
- 第二引数: 仲介するモデル
- 第三引数: 仲介モデル外部キー
- 第四引数: 最終的なモデルの外部キー
- 第五引数: ローカルキー
- 第六引数: 仲介モデルローカルキー
ポリモーフィック関係
テーブル構成
Posts
Id integer
Title string
Body text
Videos
Id integer
Title string
Url string
Comments
Id integer
Body text
Commentable_id integer
Commentable_type string
Commentable_id = post,videoのid
Commentable_type = モデル名
Public function commentable()
{
Return $this->morpTo();
}
Public function comments()
{
Return $this->morpMany('App\Comment', 'comentable');
}
Public function comments()
{
Return $this->morpMany('App\Comment', 'commentable');
}
Commentable_typeで識別するモデル名を変更したい場合
デフォルトだと、識別しやすい、App\Post, App\Videoになってしまう
Relation::morpMap([
'posts' => 'App\Post',
'Video' => 'App\Video',
]);
ポリモーフィック関係 多対多
Public function tags()
{
Return $this->morphToMany('App\Tag', 'taggable');
}
Public function posts()
{
Return $this->morphedByMany('App\Post', 'taggable');
}
Public function videos()
{
Return $this->morphedByMany('App\Video', 'taggabele');
}
## 参考
[Laravel公式ドキュメント] (https://readouble.com/laravel/5.5/ja/eloquent-relationships.html)
Laracasts -Eloquent Relationships-