Eloquentリレーション入門

# Eloquentリレーション入門
大抵のテーブルは他のテーブルに関連づいている。ユーザー情報一つにしても、個人情報から、クレカ情報など様々なテーブルのデータで形成されている。
Eloquentはそうしたリレーションを簡単に管理し操作できるようにしている。

## リレーションの仕方
モデル・テーブル作成
php artisan make:model Profile -m
Profileモデルとそれに紐付いたmigrationファイルが作られる。


##１対１の関係

###HasOne
１対１の関係(ユーザーと個人情報など）

ユーザーに紐付いた個人情報のリレーションを定義

```php

Public function profile()
{
    return  $this->hasOne(Profile::class);
}
```


ユーザーを作りTinkerで確認する

```php

$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()でそれを出力。

```terminal

>>> 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引数をもたせます

```php

Public function profile()
{
    return $this->hasOne(Profile:classs, 'foreign_key');
} 
```



逆にUser.idではないカラムで紐づけたい場合は
第３引数をもたせる

```php

Public function profile()
{
    return $this->hasOne(Profile:classs, 'foreign_key', 'hoge');
} 
```

### belongsTo
上記ではUserからProfileにはアクセスすることができた。
次は、逆にProfileからUserへとアクセスする方法を定義する

```php5

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

hasOneのときとほぼ同じである。

しかし、Eloqunetでは
リレーションメソッド名　＋ _idのサフィックスをつけたものをデフォルトの外部キーにしているため、
外部キーが異なる場合はメソッドの第２引数にカスタムキー名を渡す必要がある。

```php

Public function user()
{
    return $this->belongsTo(User::class, 'foreign_key');
}
```

親キーの主キーがIDではない、もしくは子のモデルとは異なるカラムで紐づけたい場合は第３引数を渡す。

```php

Public function user()
{
    return $this->belongsTo(User::class, 'foreign_key', 'other_id');
}
```




##１対多の場合

```php

Public function posts()
{
    return $this->hasMany(Post::class)
}
```

メソッド名が複数形になるのがポイント

Eloquentのコレクションという形でアクセスできる。

```terminal

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でループをまわしてあげる。

```php

$posts = User::find(1)->posts

Foreach($posts as $post){}
```

すべてのリレーションはクエリビルダとしても働くため、クエリに条件をつけてチェーンでつなぐことができる。

```php

$posts = User::find(1)->posts()->where('title', 'hoge')->first()
```
ここでのポイントはposts()に'()'がつくこと。

HasOneと同様に、外部キーとローカルキーを与える引数に渡すことでオーバーライドできる

```php

Return $this->hasMany(Post::class, 'post_id', 'id');
```


##１対多(Inverse)
先程は、１→多だったが今度はその逆、多→１である。
Postに対して多くのCommentが紐付いており、そのCommentからPostを取得する場合

```php

Public function post()
{
    Return $this->belongsTo(Post::class);
}
```

1対1の時と同様。

リレーションが定義できたらPostの動的プロパティでPostモデルを取得する

```php

$comment = Comment::find(1);

$comment->post->title;
```



## 多対多　の関係
ユーザーと役目の関係
    ユーザーが多くの役目をもっている
　　　　役目も多くのユーザーに共有されている

```php

Public function roles()
{
    return $this->belognsToMany(Role::class);
} 
```

```php

return $this->belongsToMany(Role::class, 'role_user', 'user_id', 'role_id')
```

* 第二引数： 結合テーブル名
* 第三引数：　リレーションを定義しているモデルの外部キー名
* 第四引数：　結合するモデルの外部キー名

中間テーブルのカラム取得
多対多リレーションには中間テーブルが必要
モデルのPivot属性を使うことにより中間テーブルにアクセスできる

```php

$user = User::find(1);

Foreach ($user->role as $role)
{
    Echo $role->pivot->created_at
}
```

中間テーブルのcreated_at,updated_atタイムスタンプを自動的に保守したい場合はwithTimestampメソッドをリレーション定義

```php
Return $this->belongsToMany('App\Role')->withTimestamps
```

Pivot属性の名前を変更することができる

```php

Return $this->belongsToMany('App\Product')
            ->as('subscription')
            ->withTimestamp();
```

中間テーブルを利用した関係のフィルタリング

```php

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テーブルを介してだと追うことができる。

```php

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 = モデル名

```php
Public function commentable()
{
    Return $this->morpTo();
}
```

```php

Public function comments()
{
    Return $this->morpMany('App\Comment', 'comentable');
}
```

```php

Public function comments()
{   
    Return $this->morpMany('App\Comment', 'commentable');
}
```

Commentable_typeで識別するモデル名を変更したい場合
デフォルトだと、識別しやすい、App\Post, App\Videoになってしまう

```php

Relation::morpMap([
    'posts' => 'App\Post',
    'Video' => 'App\Video',
]);
```



ポリモーフィック関係　多対多

```php

Public function tags()
{
    Return $this->morphToMany('App\Tag', 'taggable');
}
```

```php

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-](https://laracasts.com/series/eloquent-relationships)