LoginSignup
134
132

More than 5 years have passed since last update.

Eloquentリレーション入門

Last updated at Posted at 2018-09-19

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公式ドキュメント
Laracasts -Eloquent Relationships-

134
132
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
134
132