【初心者向け】丁寧すぎるRails『アソシエーション』チュートリアル【幾ら何でも】【完璧にわかる】


RubyやRailsを学び始めてひと月とちょっとが経ち、色々見よう見まねで作ってはみたものの、未だに __いいね機能__ や __フォロー、フォロワー機能__ などの仕組みがわからず。
というか、よくよく考えてみるとどういう仕組みでSNSを使っているユーザーが投稿していて、その __投稿とユーザーが結びついているのがわかっていない__ 、という状況にありました。

こういったモデル同士、つまりユーザーやその投稿、そして投稿につけられる"いいね"やユーザーに対するフォローなどの 
__関連付け__ の事を __アソシエーション__ と言うのですが、そのような概念がプログラミング初心者の方にはとても難しく感じるのではないか？という動機からこのような記事を執筆するに至りました。

正直、アソシエーションやリレーションの記事などは、SQLから学ぶのなどもう既にたくさん出ています。
ですが、意外とテキストベースの解説が多く個人的には初心者がとっつきにくいような気がしており、なるべく図などを使ってわかりやすく説明できたらなと思っております(画像はほとんど @mopiemon が作ってくれました🙇‍♂️感謝)。


## 目次

| 目次        |
|:-----------|
| [アソシエーションとは](#アソシエーションとは)|
| [データベース設計とは](#データベース設計とは少し難しい話です)|
| [UserとTweetの実装(１対多)](#ログイン機能とツイート機能をer図を使って設計しよう)|
| [Favoriteの実装(多対多)](#お気に入り機能をer図を使って設計しよう)|
| [Follow機能の実装(自己結合多対多)](#フォローフォロワー機能をer図を使って設計しよう)|
| [完成版のソースコード](#完成版のソースコード)|

## この記事を読むにあたっての前提
・CRUDを理解している
・MVCをざっくり理解している
・特に、Model, DataBase, Table周りの意味も簡単に知っている。

もちろん、それ以外の方も是非是非挑戦してみてください。理解なんて後から帳尻合わせていきましょう。

## できるようになること

- これが作れるようになる(お粗末ですいません) - https://rails-association-tutorial.herokuapp.com

丁寧すぎるherokuへのデプロイの方法はこちら - [【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】](https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39)

- 簡単な設計ができるようになる。
- ユーザー、投稿、イイネ、コメント、フォロー機能のアソシエーションが理解でき、実装できるようになる。


## 注意してほしいこと

- validationは説明しません(ごめんなさい)
- dependent系のオプションも説明しません(この記事読んだあと調べればすぐわかります。)
- 例外処理もしません
- indexとかも貼りません

本当にアソシエーション周りだけの理解をするための記事です。ご了承ください。
validation, dependent, viewのパーシャル化だけ実装したコードは最後に上げておきます。

## アソシエーションとは

| 目次        |
|:-----------|
| [ __アソシエーションとは__ ](#アソシエーションとは) ←今ココ|
| [データベース設計とは](#データベース設計とは少し難しい話です)|
| [UserとTweetの実装(１対多)](#ログイン機能とツイート機能をer図を使って設計しよう)|
| [Favoriteの実装(多対多)](#お気に入り機能をer図を使って設計しよう)|
| [Follow機能の実装(自己結合多対多)](#フォローフォロワー機能をer図を使って設計しよう)|
| [完成版のソースコード](#完成版のソースコード)|

https://railsguides.jp/association_basics.html
 __モデル間の関連付け__ に使われる概念です。
モデル間の関連付けと言われてもピンとこない人が多いと思うので(自分がそうでした)、図を駆使しながら具体例で考えてみましょう。

プログラミング初心者のAさんがあるブログアプリを作ったとします。そのブログは最初はAさん一人で使うものだったため、記事投稿機能のみがつけられていて、モデルも __Articleモデル__ 一つのみ実装されていました。

<img width="557" alt="qiita1.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/0c6606a0-4816-bd9b-8af2-b126ac56bdae.png">


ですが、そのサイトをたまたま見つけてしまったBさんが、勝手に自分の投稿をし始めてしまいました。

<img width="630" alt="qiita2.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/cf6c2afa-09f2-777c-3b7e-859f0489e5b0.png">


これでは __どれが誰の投稿かわからなくなってしまう__ 、とAさんはブログに __ユーザーログイン機能__ を付け足しました。これでアプリには __Articleモデル__ と __Userモデル__ の二つモデルが実装されていることになります。

<img width="819" alt="qiita3.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/a5b4af5d-3f09-aae2-4a41-253d1144611f.png">


そして、この時にまさしく __どれが誰の投稿なのか__ を関連付けるものが __アソシエーション__ なのです。

<img width="818" alt="qiita4.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/c76aded3-bcf2-aec6-fb01-2148019d61d2.png">


そして、アソシエーションを行う際は、その __モデル同士の親子関係__ がどのようになっているかがとても大事になってきます。

### モデル同士の親子関係とは

図で考えてみましょう。
Aさん(User)は、自分が作ったブログサイトでたくさんの記事(Article)を投稿します。

<img width="736" alt="qiita5.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/4102594c-b079-957c-7d90-b8d5e39042c5.png">


Bさんも少しは投稿します。

<img width="769" alt="qiita6.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/39f16cb7-0552-baa6-c999-53f68842c0f7.png">


つまり、 __User一人一人は沢山のArticleを持っている(*User has many articles.*)__ 、と考えることができます。(この突然出てきた英文は伏線ですよ！！！！)

![qiita6.5.png](https://qiita-image-store.s3.amazonaws.com/0/208038/9297aa73-e578-297a-3ede-5d57beee1a1f.png)

逆の立場(Article)も考えてみましょう。
ある日投稿された記事(Article)は、Aさん(User)によって書かれました。

<img width="666" alt="qiita7.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/0f64ed52-1d61-200a-0981-85b0a684c040.png">

次の日投稿された記事はBさんによって書かれました。
他の日に投稿された記事も、それぞれAさん、Bさんどちらかによって書かれた記事です。 __Aさん、Bさんが共作で書いた記事というのはありえません__ 。

つまり、 __Articleは誰か一人のUserに所属している(*Article belongs to a user.*)__ と考えることができます。

![qiita7.5.png](https://qiita-image-store.s3.amazonaws.com/0/208038/ebc8c7be-d9c7-f3bc-2346-52c22b0488d3.png)

UserがいなくてはArticleは生まれないし、Articleは必ず誰か一人のUserから生まれます。そう、 __Userが親でArticleが子__ となっているわけなのです。これが親子関係です。

このような関係をUserとArticleは __一対多の関係__ または __1:Nの関係__ といいます。もちろんUserが1でArticleが多です。(これがめちゃめちゃ大事です！！！！！！！！！！)

他にも __一対一(1:1)__ の関係や __多対多(M:N)__ の関係などもあります。
一対一は簡単なので割愛します。多対多は少し難しいですが、これからやっていくチュートリアルで登場&解説するので、ご安心ください。

さあ、それではいざアプリ制作に取り掛かっていきたいのですが、その前に __データベース設計__ を行わなくてはなりません。
そう、 __アソシエーションはデータベース設計と密接に関わっております__ 。


## データベース設計とは？（少し難しい話です）

| 目次        |
|:-----------|
| [アソシエーションとは](#アソシエーションとは)|
| [ __データベース設計とは__ ](#データベース設計とは少し難しい話です) ←今ココ|
| [UserとTweetの実装(１対多)](#ログイン機能とツイート機能をer図を使って設計しよう)|
| [Favoriteの実装(多対多)](#お気に入り機能をer図を使って設計しよう)|
| [Follow機能の実装(自己結合多対多)](#フォローフォロワー機能をer図を使って設計しよう)|
| [完成版のソースコード](#完成版のソースコード)|


 __情報システムにおいて、どのような情報をデータベースに格納すべきかを検討し、その「格納すべき情報」を「どのような形で保持するか？」を設計すること__ です。

一般化した定義のためわかりづらいかもしれませんが、

 - __情報システム__ は __Twitterアプリそのもの__ 
 - __どのような情報__ は
    - __ユーザー__ (のemailやpassword)
    - __tweet__ (本文やどのユーザーが投稿したのか？など)
    - __お気に入り__ など
 - __「格納すべき情報」を「どのような形で保持するか?」__ というのは
    - __情報システムをより細かく考えたデータ__
        - つまりユーザーでいうと「emailは __文字列型__ 、その識別idは __整数型__ 」など

といったアプリの動的な部分の設計のことを言います。データのことをちゃんと詳しく決めよう！！ということです。

そして、データベース設計において大事な概念が４つあります。

- エンティティ
- 属性
- 関連（リレーション）
- 関連の多重度

順番に説明して行きますが、完全に理解しなくて大丈夫ですので軽〜く読んでみてください。

#### エンティティ
エンティティとは、モデリングの対象となるアプリの中で管理する必要がある情報です。ツイッターでいうとUser, Tweet, Favorite, Followなどがこれにあたります。RailsのModelに近いですね。(実際は違います！)
そして項目を見るとわかると思いますが、 __エンティティはアプリの要件定義と表裏一体__ なのです。ここら辺は調べるといくらでも深い記事が出てくるので割愛します。


<img width="535" alt="qiitaER1.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/a6cbbe5f-abf9-ed4a-20eb-c9d3d770d18b.png">


#### 関連（リレーション）
関連（リレーション）とは、結び付きのあるエンティティ同士をリンクさせるものです。先ほどの例だとUserとArticle(ツイッターでいうとUserとTweet)が簡単な関連となります。
ここで、「あれ、関連付けって、アソシエーションっていうじゃないの？」と思う方もいらっしゃると思いますが、アソシエーションとリレーションは同じものと思って大丈夫です。

<img width="563" alt="qiitaER2.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/f23607e5-5ebd-7712-a4fa-10a2d09690d2.png">


#### 属性(プロパティ)
属性とは、あるエンティティ(Model)に従属する項目のことで、 __エンティティを1つに定めたときに、一緒に分かる情報__ だと思ってください。例えば、あるユーザーを指定したら、そのユーザーのemailやアカウント名などがこれにあたります。 __テーブルのカラム__ と同じですね。

<img width="563" alt="qiitaER3.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/30bff048-0dae-1030-6fb9-cfc23a8e8870.png">



#### 関連の多重度
関連のあるエンティティAとBについて、片方から他方を見たときに __相手が1つなのか、複数なのか__ ということを明らかにすることです。
先ほどの例にすると、 __Userから見るとArticleは複数__ 、 __Articleから見るとUserは1つ__ ということになります。
すなわち、 __1つのAからBを見たとき__ および __1つのBからAを見たとき__ の相手の数を明らかにすることが、 __多重度を決定する__ ということになります。

<img width="629" alt="qiitaER4.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/031096be-9346-b6f8-1589-d25110350036.png">


・・・・はい、ここまでがデータベースの設計についての基本の概念であり、その中の __関連(リレーション)__ 、 __関連の多重度__ がまさにアソシエーションにあたります。
アソシエーションとデータベース設計が密接に関わっていること、お分りいただけたでしょうか。

早速難しい話からスタートしてしまいましたが、最初はわからないのは当たり前です。
実際の開発では、設計を完璧に仕上げることがほとんどの場合においてマストですが、とりあえず今回は小さく設計して実装 -> また小さく設計&実装という形で一つ一つ確認していきましょう。
では、チュートリアルに入ります！


## (ありがちだけど)twitterクローンを作ってみよう
Rails tutorialやその他シラバスなどでおなじみの、twitterクローンでアソシエーションを学んでいきます。
 __今回学びたいのはアソシエーションの部分のみ__ なので、見栄えやその他細かい機能は必要最低限で行こうと思います。ご了承ください。

それではまずは設計です。設計の時によく使われるのが、 __ER図__ と いうものです。

### ER図とは
データベース設計（データモデリング）で使う設計手法です。 __お絵かきの方法__ です。
ER図は、データベースが必要なWEBサイトやシステムの設計では必ずと言ってよいほど作成されます。逆に言うと、ER図なしにはデータベースを構築できません。データベース設計の基本中の基本と言える設計手法です。
様々な記法があったりするのですがここでは __IE記法__ というものを採用します。(とても簡単です)

#### IE記法

データベース設計のうち、 __関連(リレーション)、関連の多重度__ の二つを決めるものです。要するに __アソシエーションを決める記号__ となります。
上記のデータベース設計で出て来た画像も、ER図であり、IE記法でかかれています。
以下三つの記号から成り立っています。

<img width="668" alt="qiitaIE.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/d47c8792-28c0-6ce8-fcde-77d84ef32317.png">

今回はこれのうち __鳥の足(多)__ を使ってツイッタークローンをシンプルに設計します。

## ログイン機能とツイート機能をER図を使って設計しよう

| 目次        |
|:-----------|
| [アソシエーションとは](#アソシエーションとは)|
| [データベース設計とは](#データベース設計とは少し難しい話です)|
| [ __UserとTweetの実装(１対多)__ ](#ログイン機能とツイート機能をer図を使って設計しよう)  ←今ココ|
| [Favoriteの実装(多対多)](#お気に入り機能をer図を使って設計しよう)|
| [Follow機能の実装(自己結合多対多)](#フォローフォロワー機能をer図を使って設計しよう)|
| [完成版のソースコード](#完成版のソースコード)|

(設計図の作成には [Cacoo](https://app.cacoo.com/ "Cacoo")というのを使用していますが、皆さんは手書きで問題ありません。)

最初に言った通り、まずは小さく設計しましょう。UserとTweetについて決めて行きます。

上述の通りエンティティ(Model)はUserとTweetの二つになります。
次にプロパティです。
Userはdeviseというgemを使う予定なので、デフォルトのカラムであるemailとpassword(deviseでは`encrypted_password`という名前になっています。)を使って行きましょう。
Tweetは本文(body)のみで大丈夫でしょう。

とりあえずここまでを図に落とし込んでみましょう。

<img width="399" alt="qiita8.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/5a204a50-cc12-6b52-aad4-cc9c05d57856.png">


次にアソシエーション部分である、 __関連__ と __関連の多重度__ についてです。

UserとTweetは最初に出した例であるUserとArticleの関係と同じであり、 __一つのUserはTweetを複数持っています。__

つまり、UserとTweetは一対多(1:N)の関係というわけです。

なので、ER図はこのようになります。

![qiita9.png](https://qiita-image-store.s3.amazonaws.com/0/208038/019f9288-d565-08ef-156c-1b30ddb671d4.png)


ただ、ここで注意するべきことがあります。このままだと __TweetがどのUserに所属しているのか__ という情報がありません。

それを設定するために、foreign_keyを設定する必要があります。

#### foreign_keyとは
まず、データには一つ一つ識別するためのidがあります。これを __Primary Key(主キー)__ と言い、Railsでは __id__ というカラム名でテーブル作成時に標準搭載されています。

そして、 __foreign key__ というのはその __親のid(Primary key)を保存するカラム__ というわけです。
これにより __どの親に所属しているのか？__ というのを識別することができます。

![qiita10.png](https://qiita-image-store.s3.amazonaws.com/0/208038/684a19fa-712f-4190-aa7c-23ce8b982814.png)


(赤丸の`user_id`が`foreign_key`)


なのでプロパティにforeign_keyを追加しましょう。 __親のモデル名_id__ という名前とするとRailsの命名規則により色々便利になるので、 __user_id__ としましょう。
ER図はこのようになります。

![qiita11.png](https://qiita-image-store.s3.amazonaws.com/0/208038/9d7addbc-5ca9-747a-612d-b3a0bb134d3b.png)


これでUser,Tweet部分の設計は完了しました！！では実装して行きましょう。


## ようやくプログラミング！

### 設計の通りに下準備

それではアプリを作成し、ER図の通りにUserとTweetについて準備してきましょう。

※1  __$__ マークはターミナルのコマンドという意味です。実際には打たないでください。
※2  __#~~__ はコメントアウトです。打たなくて大丈夫です。

~~~terminal:ターミナル
$rails new association_tutorial
$cd association_tutorial/
~~~

Userはログイン機能をつけるため、deviseというgemを使用します。(deviseの詳しい説明は割愛します。[こちら](https://qiita.com/cigalecigales/items/f4274088f20832252374)の記事がおすすめです。)

Gemfileに以下の記述をしましょう。(色々書いてあると思いますが消さずに追加してください。)

~~~ruby:Gemfile
gem 'devise'  # 一番下に追加
~~~

ターミナルに戻り、以下のコマンドを打ってください。deviseの機能をインストールし、 __その機能を取り込んだ__ Userモデルを作成します。 

~~~terminal:ターミナル
$bundle install  # gemのダウンロード
$rails g devise:install  # deviseの機能のインストール
$rails g devise User  # deviseの機能を取り込んだUserモデルの作成
$rails g controller users index show #(deviseの機能ではない)usersコントローラの作成。今回はindexとshowアクションを用意
~~~

カラム(プロパティ)は、deviseのデフォルトのカラムが`email`と`password`となっており、設計と同じなのでこのままで問題ありません。

次にTweetについてです。(`foreign_key`である`user_id`もこのタイミングで追加しています。)

~~~terminal:ターミナル
$rails g model Tweet body:text user_id:integer # 要件通りにカラムとその型も一緒に定義。
$rails g controller tweets new index show create
~~~

忘れずにテーブルの確定をしましょう。

~~~terminal:ターミナル
$rails db:migrate
~~~

以下のように表示されればOKです。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/208038/96490e70-3fed-1fc0-212b-c81b79529fa2.png)

そしてルーティングの設定です。以下のように記述してください。

~~~ruby:config/routes.rb
Rails.application.routes.draw do

  root 'tweets#index'  # 追加

# ===========ここはいらないので削除orコメントアウト==========
  #get 'tweets/new'
  #get 'tweets/index'
  #get 'tweets/show'
  #get 'tweets/create'
  #get 'users/index'
  #get 'users/show'
# ==================================================

  devise_for :users
  resources :tweets  # 追加
  resources :users  # 追加
  
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
~~~

tweets, usersコントローラもサクッと表示部分を書いておきます。

~~~ruby:app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  before_action :authenticate_user!, except: [:index]  # deviseのメソッドで「ログインしていないユーザーをログイン画面に送る」メソッド
  def new
    @tweet = Tweet.new # 新規投稿用の空のインスタンス
  end

  def create
    # 後で書きます。
  end

  def index
    @tweets = Tweet.all
  end

  def show
    @tweet = Tweet.find(params[:id])
  end
end
~~~

~~~ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
  end
end
~~~

最後に、レイアウトなどの各ページも作っておきましょう。(フロントがぐちゃぐちゃなのは本当にごめんなさい！パーシャル化はあえてしておりません。)

~~~erb:app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
  <head>
    <title>AssociationTutorial</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>

    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>

  <body>
  <!-- 追加 -->
    <% if user_signed_in? %>  <!-- deviseのメソッドで、「ログインしているかしていないかでtrue or falseを返す」メソッド -->
      <%= link_to "新規投稿", new_tweet_path %>
      <%= link_to "ログアウト", destroy_user_session_path, method: :delete %>
      <%= link_to "マイページ", user_path(current_user.id) %>
    <% else %>
      <%= link_to "新規登録", new_user_registration_path %>
      <%= link_to "ログイン", new_user_session_path %>
    <% end %>
      <%= link_to "ツイート一覧", tweets_path %>
      <%= link_to "ユーザー一覧", users_path %>
  <!-- ここまで -->
    <%= yield %>
  </body>
</html>
~~~

~~~erb:app/views/tweets/new.html.erb
<h1>Tweets#new</h1>
<p>Find me in app/views/tweets/new.html.erb</p>

<%= form_for @tweet do |f| %>
  <p>
    <%= f.label :body,"ツイート" %>
    <%= f.text_field :body %>
  </p>
  <%= f.submit %>
<% end %>
~~~

~~~erb:app/views/tweets/index.html.erb
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>

<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
~~~

~~~erb:app/views/tweets/show.html.erb
<h1>Tweets#show</h1>
<p>Find me in app/views/tweets/show.html.erb</p>

<p><span>ツイート内容: </span><%= @tweet.body %></p>
~~~

~~~erb:app/views/users/index.html.erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%=link_to user.email, user_path(user.id) %></p>
<% end %>
~~~

~~~erb:app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>
~~~

### いよいよアソシエーションを記述しよう

さあ、いよいよアソシエーションの記述になります。ER図で言うと棒線の部分がこれにあたります。

・・・と、いってもRailsは命名規則によってものすごくよしなにやってくれるので、記述することはとても少ないです。

#### has_many

 __UserはたくさんのTweetを持っています(_User has many tweets._)__ 。なので、 __Userモデル__ に以下の記述をしてください。

~~~ruby:app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets  # これを追加
end
~~~

このように __子の方のモデル名の複数形__ を書きます。has_manyの方が __複数形__ なことは __Railsの命名規則でマスト__ ですのでお気をつけください。(英文と同じですね！)

#### belongs_to

 __Tweetは一つのUserに所属しています(_Tweet belongs to a User._)__ 。
もうおわかりですね。tweetモデルに以下のように記述します。

~~~ruby:app/models/tweet.rb
class Tweet < ApplicationRecord
    belongs_to :user  # これを追加
end
~~~

はい、こちらは __一つ__ なので __単数形__ です。

そして、これだけの記述だけでアソシエーションをすることができています。tweetsテーブルに追加してある __foreign_key__ の __user_id__ も、 __モデル名_id__ としたおかげで勝手に認識してくれています。railsはこういったところがとてもすごいですね。

#### コントローラの編集
さて、userとtweetをアソシエーションさせた状態で保存させたいです。なので、tweetを保存するタイミング、要するに`tweets#create`とストロングパラメータ(`tweets_params`)を以下のように記述しましょう。

~~~ruby:app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  before_action :authenticate_user!, except: [:index]  # deviseのメソッドで「ログインしていないユーザーをログイン画面に送る」メソッド

  def new
    @tweet = Tweet.new  # 新規投稿用の空のインスタンス
  end

  def create
# ============追加================
    @tweet = Tweet.new(tweet_params)  # フォームから送られてきたデータ(body)をストロングパラメータを経由して@tweetに代入
    @tweet.user_id = current_user.id # user_idの情報はフォームからはきていないので、deviseのメソッドを使って「ログインしている自分のid」を代入
    @tweet.save
    redirect_to tweets_path
# =================================
  end

  def index
    @tweets = Tweet.all
  end

  def show
    @tweet = Tweet.find(params[:id])
  end
# ===============追加=============
  private
    def tweet_params
      params.require(:tweet).permit(:body) # tweetモデルのカラムのみを許可
    end
# =================================
end
~~~

ここで注意したいポイントは、 __user_idの情報はフォームから送られていないので、追加で代入してあげないといけない__ と言うことです。

この場合はdeviseのメソッドである`current_user`で __ログイン中のユーザーの情報__ が取得できるため(つまりツイートした本人)、`current_user.id`を`@tweet.user_id`に代入しています。

これで、アソシエーションをした状態でデータの保存をすることができました！
とりあえず何人か新規登録して、投稿してみましょう。
deviseの新規登録画面 - http://localhost:3000/users/sign_up
新規投稿画面 - http://localhost:3000/tweets/new

ログをみても、きちんとuser_idに代入されているのがわかると思います。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/208038/4cfeaa4e-5f38-3b8d-d46d-a3e5fc300f68.png)


#### アソシエーションしているデータの受け取り方
ここまででアソシエーションした状態でデータ保存をすることができました。
次は「 __ユーザーがツイートしたデータ__ 」や、「 __このツイートをしたユーザー__ 」などといった __表示の方法__ です。

##### Userがtweetしたデータ
結論から言うと、

~~~ruby:例
@user.tweets
~~~

(`@user`は一つのユーザーのデータです。)
これで「ユーザーに関連したツイート」を取得することができます。
`tweets`と複数形になっていることに注意してください。この記述はUserモデルに記述した`has_many :tweets`によって決まっています。
そして、複数形になっていることからもわかりますが、 __@user.tweetsは複数のツイートが入った配列__ となっております。

これらを用いて、`Users`のコントローラとビューを以下のように変更しましょう。

~~~ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
# ===============追加==============
    @tweets = @user.tweets
# ================================
  end
end
~~~

~~~erb:app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>

<!-- 追加 -->
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
~~~

http://localhost:3000/users/1
このようになっていればうまくいっています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/208038/f2f0074c-ef07-272c-77f9-85051a6dff76.png)



##### このtweetをしたUser
こちらも同じように

~~~ruby:例
@tweet.user
~~~

このようにすれば「このツイートをしたユーザー」を取得することができます。
こちらもTweetモデルに記述した`belongs_to :user`より、 __単数__ にすることで取得します。

そして、こちらは __単数__ なので、 __取得したデータも一つ__ のみです。

なので、Tweetsコントローラとビューは以下のようになります。

~~~ruby:app/controllers/tweets_controller.rb
class TweetsController < ApplicationController
  before_action :authenticate_user!, except: [:index]  # deviseのメソッドで「ログインしていないユーザーをログイン画面に送る」メソッド

  def new
    @tweet = Tweet.new  # 新規投稿用の空のインスタンス
  end

  def create
    @tweet = Tweet.new(tweet_params)  # フォームから送られてきたデータ(body)をストロングパラメータを経由して@tweetに代入
    @tweet.user_id = current_user.id # user_idの情報はフォームからはきていないので、deviseのメソッドを使って「ログインしている自分のid」を代入
    @tweet.save
    redirect_to tweets_path
  end

  def index
    @tweets = Tweet.all
  end

  def show
    @tweet = Tweet.find(params[:id])
# ===============追加==============
    @user = @tweet.user
# ================================
  end

  private
    def tweet_params
      params.require(:tweet).permit(:body) # tweetモデルのカラムのみを許可
    end
end
~~~

~~~erb:app/views/tweets/show.html.erb
<h1>Tweets#show</h1>
<p>Find me in app/views/tweets/show.html.erb</p>

<p><span>email: </span><%= @user.email %></p> <!-- 追加 -->
<p><span>ツイート内容: </span><%= @tweet.body %></p>
~~~

indexもemailを表示させちゃいましょう。

~~~erb:app/views/tweets/index.html.erb
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>email: </span><%=link_to tweet.user.email, user_path(tweet.user.id) %></p> <!-- 追加 -->
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
~~~

ちょっと見慣れないかもしれないですが、このように`tweet.user.email`など、メソッドチェーンで関連先を一気に取得することも可能です。(あまり長くなりすぎるとプログラムの可読性が悪くなるので注意！)

これで一対多の実装ができました！！！ひとまずお疲れ様です。

## 一対多まとめ

- 親子関係を考え、 __子供に親のid(foreign_key)が入るカラム__ を追加
- 親側モデルに __has_many :子供のモデル名の複数形__ 、子供側モデルに __belongs_to :親のモデル名(単数形)__ と記述
- 関連したモデルの情報は、 __`関連元のモデルのインスタンス.関連先のモデル名`__ で取得可能。`関連先のモデル名`は、 __関連元のモデルに記述した関連先の名前__ が入る。(has_manyなら複数、belongs_toなら単数。)


## お気に入り機能をER図を使って設計しよう

| 目次        |
|:-----------|
| [アソシエーションとは](#アソシエーションとは)|
| [データベース設計とは](#データベース設計とは少し難しい話です)|
| [UserとTweetの実装(１対多)](#ログイン機能とツイート機能をer図を使って設計しよう)|
| [ __Favoriteの実装(多対多)__ ](#お気に入り機能をer図を使って設計しよう)   ←今ココ|
| [Follow機能の実装(自己結合多対多)](#フォローフォロワー機能をer図を使って設計しよう)|
| [完成版のソースコード](#完成版のソースコード)|

さて、次は __お気に入り機能__ の実装です。ここまででめちゃめちゃ長くなってしまいましたが、気合いで頑張っていきましょう！

### お気に入り機能とはどういう機能なのか？


お気に入りとは、 __ユーザーがツイートに対してつける印__ だとみなすことができます。

また片方ずつ考えていきましょう。
ユーザーは「たくさんのツイートをお気に入り」することができます。

![qiita12.png](https://qiita-image-store.s3.amazonaws.com/0/208038/5953d7ae-f099-229e-b3ab-b5685345766e.png)


逆も考えてみます。
「ツイートはたくさんのユーザーにお気に入り」されます。

![qiita13.png](https://qiita-image-store.s3.amazonaws.com/0/208038/62b85f5b-8cd8-ce84-751e-7bb8cd469a3b.png)


このように __「ユーザーもツイートもたくさん持っている」関係を多対多(M:N)の関係__ といいます。

### 多対多(M:N)を設計しよう

それではER図に落とし込んでいきたいのですが、Railsでは多対多の関係をUserモデル、Tweetモデルのみでは実装することができません。

実装するためには、中間テーブルというものが必要です。

#### 中間テーブルとは

多対多をプログラムで実装するためには、お互いがお互いの`foreign_key`を知らなくてはなりません。

![qiita15.png](https://qiita-image-store.s3.amazonaws.com/0/208038/ba662caa-5bf2-4700-3d6a-4b1c7a2ec558.png)

ですが「複数のuserによってどんどんファボが増えていった」場合、画像のように配列にどんどん追加していくか、カラムをtweetsテーブルにどんどん追加しなくてはならず、非常に面倒です。

そのような状態を避けるために、 __お互いのidだけを保存する__ テーブルが必要となります。

それを、 __中間テーブル__ といいます。

これで「どうやってお気に入り数や、誰のお気に入りなのかを判断するの？」と思うかもしれませんが、それは __中間テーブルに保存される`tweet_id`と`user_id`で判断__ しています。

![qiita16.png](https://qiita-image-store.s3.amazonaws.com/0/208038/97ca3c8d-2c8d-d0c2-7dbe-84551297dbee.png)

例えば、「あるツイートについたお気に入り数」をみたいときは、 __その`tweet_id`と同じレコードに保存されている`user_id`の数__ を見ればよく、

「あるユーザーのお気に入りしたツイート」がみたいときは、 __その`user_id`と同じレコードに保存されている`tweet_id`から、ツイートを検索__ すれば良いことになります。

#### 中間テーブルを使ってER図の作成

それでは中間テーブル込みのER図を作っていきましょう。

通常、中間テーブルは`テーブル1_テーブル2の複数系`(`user_tweets`)などとつけることが多いですが、今回中間テーブルの意味が「お気に入り登録」とはっきりわかっているため、`favorites`テーブルとしてしまいましょう。

![qiita18.png](https://qiita-image-store.s3.amazonaws.com/0/208038/70562202-afc6-490f-f448-7b6b77dd8ab6.png)


中間テーブルは __たくさん持たれる側(どちらにもbelongs_to)__ なのに注意してください。
それでは、これを実装していきましょう！

### 設計の通りに下準備

設計の通り、Favorite関連のコントローラ、モデル、テーブルを作成します。

```linux:ターミナル
$rails g model Favorite user_id:integer tweet_id:integer
$rails g controller favorites create destroy
$rails db:migrate
```
![image.png](https://qiita-image-store.s3.amazonaws.com/0/208038/023d002c-941f-9cd1-d391-1ae59eb5d20b.png)

あとはルーティングですが、後々のことを考えて、`tweets`にネストしたルーティングを作成しましょう。
下記のように __do ~ endの中に入れる形で書く__ ことを、 __ネスト(入れ子)する__ と言います。


~~~ruby:config/routes.rb
Rails.application.routes.draw do
  #==================削除orコメントアウト================
  # get 'favorites/create'
  # get 'favorites/destroy' 
  #=====================================
  root 'tweets#index'
  devise_for :users

  # ================ここをネスト(入れ子)した形に変更
  resources :tweets do
    resource :favorites, only: [:create, :destroy]
  end
  #======================================

  resources :users 
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end

~~~

理由としては、 __お気に入り時に、`foreign_key`の一つである`tweet_id`を取得するのが楽になるから__ です。(一対多の時もそうでしたが、`foreign_key`を取得しないとリレーションができません。)


詳しくはこちら - [Railsのルーティングを極める(後編)](https://techracho.bpsinc.jp/baba/2014_03_03/15619)

また、favoriteの詳細ページは作らない、つまりfavoriteのidは要らず省略したいため、`resource`と、単数形のメソッドを利用しています。

`rails routes`でルーティングを確認すると、以下のようにfavoritesのcreateとdestroyに`:tweet_id`が含まれ、かつ`:id(:favorite_id)`が省略された形で生成されているのがわかると思います。

<img width="1094" alt="スクリーンショット_2018-12-02_20_17_13.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/1a21be3c-bc4a-eba5-c95b-b461c3a846fc.png">

これで下準備は完了です。

###アソシエーションの記述

中間テーブル`favorites`は __たくさん持たれる側__ なので、このようになります。

~~~ruby:app/models/favorite.rb
class Favorite < ApplicationRecord
    belongs_to :user
    belongs_to :tweet
end
~~~

したがって、User, Tweetモデルも以下のようになります。

~~~ruby:app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets
  has_many :favorites  # 追加
end
~~~

~~~ruby:app/models/tweet.rb
class Tweet < ApplicationRecord
    belongs_to :user
    has_many :favorites  # 追加
end
~~~

これでアソシエーションが組めたので、
`@user.favorites`や`@tweet.favorites`などで __それぞれに関連したお気に入り(実際にはuser,tweetのidの2つ組の情報)__ が取得できるようになりました。
・・・が、viewを作る前に、一つ注意しなくてはならないことがあります。

お気に入り機能は __登録だけでなく解除__ もできなくてはならないことです。
そのため、Tweetモデルに「このツイートをユーザーがファボしているかどうか」を判定するメソッドを用意しましょう。

#### ユーザーがツイートをお気に入りしたかどうかの判定メソッド
「ツイートがファボしてあるかどうか」を判定したいので、 __Tweetモデル__ に以下のように記述しましょう。

~~~ruby:app/models/tweet.rb
class Tweet < ApplicationRecord
    belongs_to :user
    has_many :favorites

# 追加
    def favorited_by?(user)
        favorites.where(user_id: user.id).exists?
    end
end
~~~

これは、Tweetのインスタンスメソッドなので、ビューなどで

~~~ruby
if @tweet.favorited_by?(current_user)
~~~

という風に使うことができます。これはつまり

~~~ruby
if @tweet.favorites.where(user_id: current_user.id).exists?
~~~

ということと同じなので、 __アソシエーションの記述である`@tweet.favorites`の中に、引数で送られた`user`のidがあるかどうか？__ ということを判定していることになります。

インスタンスメソッドについてはこちら - [Rubyのクラスメソッドとインスタンスメソッドの例](https://qiita.com/tbpgr/items/56eb65c0ea5882abbb07)

それではお気に入りボタンを作っていきましょう。

#### お気に入りボタンの実装

tweetsのindexに実装します。先ほど定義した`favorited_by?`を早速使います。
`count`メソッドで、 __配列の中の要素数__ を取得しており、そしてそれがそのまま __お気に入りの数__ となります。

~~~erb:app/views/tweets/index.html
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>email: </span><%=link_to tweet.user.email, user_path(tweet.user.id) %></p>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
  <!-- 追加 -->
  <% if user_signed_in? %>
    <% if tweet.favorited_by?(current_user) %> <!-- ログインしているユーザーがファボしたかどうかで分岐 -->
        <p><span>お気に入り解除: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :delete %></p>
    <% else %>
        <p><span>お気に入り登録: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :post %></p>
    <% end %>
  <% else %>
    <p><span>お気に入り数: </span><%= tweet.favorites.count %></p>
  <% end %>
  <!-- ここまで -->
<% end %>
~~~
お気に入り登録の時は`method: :post`
お気に入り解除の時は`method: :delete`にすることに注意してください。`rails routes`で`Verb`のところをきちんと確認しましょう。

<img width="1094" alt="スクリーンショット_2018-12-02_20_17_13.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/47fce343-2c2d-0175-b476-b39a4bc81b9b.png">

あとはコントローラです。

#### FavoritesControllerの実装

繰り返しますが __URIに:tweet_id__ が含まれており、`tweet_favorites_path(tweet.id)`と引数でtweetのidを送ってあるので、 __`params[:tweet_id]`__ とすれば __お気に入り登録しようとしているツイートのidが取得できます__ 。

~~~ruby:app/controllers/favorites_controller.rb
class FavoritesController < ApplicationController
  def create
    # こう記述することで、「current_userに関連したFavoriteクラスの新しいインスタンス」が作成可能。
    # つまり、favorite.user_id = current_user.idが済んだ状態で生成されている。
    # buildはnewと同じ意味で、アソシエーションしながらインスタンスをnewする時に形式的に使われる。
    favorite = current_user.favorites.build(tweet_id: params[:tweet_id])
    favorite.save
    redirect_to tweets_path
  end

  def destroy
    favorite = Favorite.find_by(tweet_id: params[:tweet_id], user_id: current_user.id)
    favorite.destroy
    redirect_to tweets_path
  end
end
~~~

ポイントは

~~~ruby
current_user.favorites.build(tweet_id: params[:tweet_id])
~~~

です。これは、 __current_userに関連したFavoriteインスタンスを生成__ しています。なので、Favoriteのプロパティはすでに __user_id: current_user.id__ が代入されております。
あとは残りの`tweet_id`にviewから送られてきた`params[:tweet_id]`を代入するだけです。(`params[:id]`ではないことに注意！ルーティングで生成されるURIを見れば`params[:tweet_id]`であることを確認できます。)

これにより、お気に入り登録&解除を行うことができるようになりました！
http://localhost:3000/tweets

#### has many through

中間テーブルに保存されている情報は __それぞれの親id__ でしかないので、直接取得しviewに表示しようと思うと、少しコツがいります。

~~~ruby:例
# コントローラ
  def show
    @user = User.find(params[:id])
    @tweets = @user.tweets
    # mapメソッドを使いfavoriteをtweetの情報に変換
    @favorite_tweets = @user.favorites.map{|favorite| favorite.tweet}
  end
end
~~~

~~~erb:例
<!-- ビュー -->
<% @favorite_tweets.each do |tweet| %>
  <hr>
  <p><span>ファボツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
~~~

このように、[mapメソッド](https://techracho.bpsinc.jp/hachi8833/2018_07_20/59639)でfavoritesの情報をtweetの集まりに変換してあげなくてはいけません。

ですが、`has many through`を使えば、 __ユーザーがファボしたツイート__ を直接アソシエーションで取得することができます。

このように実装します。

~~~ruby:app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets
  has_many :favorites
  has_many :favorite_tweets, through: :favorites, source: :tweet  # 追加
end
~~~

#### source
`source`は __「参照元のモデル」__ をさすオプションです。
これを指定することで __アソシエーションでメソッドチェーンする時の名称を変更__ することができます。

本当は`has_many :tweets, through: :favorites`
と記述したいのですが、上の`has_many :tweets`と重複してしまうため、`favorite_tweets`と名称を変更しています。

#### has many throughを使った「ユーザーがファボしたツイートの表示」

これで、 __`@user.favorite_tweets`__ とやることで、「ユーザーがファボしたツイート」を取得することができるようになりました。

それでは、Usersコントローラ、ビューも以下のように変更してください。

~~~ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
    @tweets = @user.tweets
    @favorite_tweets = @user.favorite_tweets # 追加
  end
end
~~~

~~~erb:app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>

<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
<!-- 追加 -->
<% @favorite_tweets.each do |tweet| %>
  <hr>
  <p><span>ファボツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
~~~


これでファボ機能が実装できました、確認してみましょう！！
http://localhost:3000


#### コメント機能も作ってみよう
コメント機能も多対多となります。

![qiita19.png](https://qiita-image-store.s3.amazonaws.com/0/208038/cd4a2e6d-873f-4f32-ad75-f17b34097096.png)


ここまでの知識があればきっと実装することができると思うので、是非ともやってみてください！
最後にソースコードを置いておきます。

<dl>
  <dt>ヒント</dt>
  <dd>・中間テーブルに文章を入れれるカラムを追加</dd>
  <dd>・「一回コメントした人も何度もコメントできるようにするか」はあなたの自由(favorited_by?の話です)</dd>
  <dd>・form_for の引数に渡すインスタンスに注意</dd>
</dl>

ネストしてあるコントローラへのルーティングは、form_forの場合

~~~erb:例
<%= form_for([@tweet, @comment]) do |f| %>
~~~
のように、 __配列で二つ([関連元のインスタンス, 関連先のインスタンス])__ 渡す必要があります。

## 多対多まとめ

- Railsでは多対多を実装するために __中間テーブル__ が必要。
- 中間テーブル側が子供(たくさん持たれる方)となるので、 __`belongs_to :親1`  `belongs_to :親2`__ と書こう.
- 親側はもちろん __`has_many :中間テーブルの名前の複数形`__
- `has_many_through`を使うと記述が省略できるので積極的に使おう
- `has_many_through`を使うときに指定する`source`は __参照元__ のモデル名なのに注意
- 「ファボしてるかしていないか」、という判定のメソッド(今回は`favorited_by?`)を用意することに注意(コメントなど、用意しなくてもいい時もある)


### フォロー、フォロワー機能をER図を使って設計しよう

| 目次        |
|:-----------|
| [アソシエーションとは](#アソシエーションとは)|
| [データベース設計とは](#データベース設計とは少し難しい話です)|
| [UserとTweetの実装(１対多)](#ログイン機能とツイート機能をer図を使って設計しよう)|
| [Favoriteの実装(多対多)](#お気に入り機能をer図を使って設計しよう)|
| [ __Follow機能の実装(自己結合多対多)__ ](#フォローフォロワー機能をer図を使って設計しよう)   ←今ココ|
| [完成版のソースコード](#完成版のソースコード)|

いよいよラストです！（本当に長かった）最後に、ユーザーフォロー機能を追加していきます。

### フォロー、フォロワー機能とはどういう機能なのか？

結論から言うと __フォローする側のユーザー、フォローされる側のユーザーというユーザー同士の多対多__ となります。

![qiita20.png](https://qiita-image-store.s3.amazonaws.com/0/208038/95779da6-af2f-b0b2-881d-4178d9658528.png)


とりあえず多対多なので中間テーブルは必要そうです。
しかし、ご存知の通りUserモデルは一つしかなく、今のままだとフォローする側のユーザー、フォローされる側のユーザーがどれだか全くわかりません。

![qiita21.png](https://qiita-image-store.s3.amazonaws.com/0/208038/5ce79ea4-392d-07e4-e494-784d3c4712d4.png)

なので、 __ユーザーモデルをフォローする側、される側にうまく切り分ける__ ことがこの実装の鍵となります。

そしてこのような自分自身との結合のことを __自己結合__ と言います。自己結合は多対多だけでなく、一対多でもあり得ます。(雇用者モデルにおいて、管理者:従業員の１対多など)

### UserとUserの多対多(M:N)を設計しよう(自己結合)

ユーザーモデルは一旦忘れて考えましょう。
`Followingモデル`と`Followerモデル`があったとします。(フォローする方とされる方)

![qiita 21.5.png](https://qiita-image-store.s3.amazonaws.com/0/208038/aba49a8b-8c66-709a-66ce-5c9da64a4558.png)


これらはもちろん多対多で関連しているため、中間テーブルが存在します。関連を表現しているため、`Relationship`モデルと名付けます。
中間テーブルなので各親テーブルの`primary_key`を`foreign_key`として保存したいです。
なので、

__`following_id`にはFollowingモデルのidを__
__`follower_id`にはFollowerモデルのidを__
`Relationship`の`foreign_key`に設定すれば良いと言うことがわかります。

![qiita22-3.png](https://qiita-image-store.s3.amazonaws.com/0/208038/166c2949-a9be-384d-8dc3-462df8ff9acc.png)


こうすればやることは普通の多対多と同じです。

- __Followingから見て、Followerを(Relationshipを介して)集める__
- __Followerから見て、Followingを(Relationshipを介して)集める__

と言うことになります。

<img width="511" alt="qiita23.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/33e87f76-617c-1907-e458-0fa98a56bb41.png">
ここでふた通りのRelationshipを考えなくてはならないのは、プログラムで記述するときにわかります。

これをUserモデルに直して考えると、

- __フォローする側のUserから見て、フォローされる側のUserを(中間テーブルを介して)集める__
- __フォローされる側のUserから見て、フォローしてくる側のUserを(中間テーブルを介して)集める__

と言うことになります。

![qiita24.png](https://qiita-image-store.s3.amazonaws.com/0/208038/382dde7f-d29a-fe19-b6b3-711f9852ee3c.png)

ヒジョーーにややこしくなってきましたが、ここまで落とし込めば十分に __Railsで実装可能__ です。やっていきましょう！

### 設計の通りに下準備

ここは今までと同じく、設計通りにコマンドを打ち込むだけです。(中間テーブルの名前は`Relationships`としました。)

~~~terminal:ターミナル
$rails g model relationship following_id:integer follower_id:integer
$rails g controller relationships create destroy
$rails db:migrate
~~~

`favorite`の時と同じく、親であるuserのidが欲しいため、`users`の中にネストしましょう。
また、フォロー一覧、フォロワー一覧の画面も用意したいため、`follows`, `followers`と言うアクションへのルーティングを作ります。`on member`メソッドを使います。
詳細 - [Railsのルーティングを極める(後編)](https://techracho.bpsinc.jp/baba/2014_03_03/15619)

~~~ruby:config/routes.rb
Rails.application.routes.draw do
  
  #==================削除orコメントアウト================
  # get 'relationships/create'
  # get 'relationships/destroy'
  #=====================================
  root 'tweets#index' 

  devise_for :users

  resources :tweets do
    resource :favorites, only: [:create, :destroy]
  end

  # ================ここをネスト(入れ子)した形に変更
  resources :users do
    resource :relationships, only: [:create, :destroy]
    get :follows, on: :member # 追加
    get :followers, on: :member # 追加
  end
  #======================================
  # For details on the DSL available within this file, see http://guides.rubyonrails.org/routing.html
end
~~~

まだviewやコントローラも触れていませんが、先にアソシエーションの記述を完成させます。

### アソシエーションの記述

いよいよアソシエーションの記述です。

#### 中間テーブル`Relationship`モデルの設定
とりあえず、`Userモデル`と言う呼び方はやめて`Following`, `Follower`と言うモデル名に変更したいです。
そしてそれは`class_name`と言うオプションを使えば実現できます。

##### class_name
`:class_name` - _関連するモデルクラス名を指定。関連名と参照先のクラス名を分けたい場合に使う_
ものとなります。

これを使うことにより

~~~ruby
belongs_to 変更したい親モデル名, class_name: "元々の親モデル名"
~~~

とすることができます( __"元々の親モデル名"__ は __文字列__ なのに注意！)。これで`Followモデル`、`Followerモデル`を擬似的に作り出すことができそうです。

実際、Relationshipsテーブルのforeign_keyとして`following_id`、`follower_id`と名前をつけており、モデル名も`Following`, `Follower`とすれば帳尻が合います。
Relationshipモデルにそれぞれ所属先を定義しちゃいましょう。

~~~ruby:relationship.rb
class Relationship < ApplicationRecord
    belongs_to :following, class_name: "User"
    belongs_to :follower, class_name: "User"
end
~~~

#### Userモデルの設定

Relationshipモデル側に`Following`, `Follower`モデルに所属している、と言うことを定義してありますので、それをうまく使えば良さそうです。

ここで、設計の時に決めた自己結合の文句を確認してみましょう。

- __フォローする側のUserから見て、フォローされる側のUserを(中間テーブルを介して)集める。__
- __フォローされる側のUserから見て、フォローしてくる側のUserを(中間テーブルを介して)集める。__

です。
ですが、とりあえずこの通りに記述しようとしても、`has_many :relationships`をふた通り書かなくてはならないため、名前被りが起きてしまいます。なので、__フォローする側、される側ふた通り__の中間テーブルの名前を再定義しなくては行けなさそうです。


今回は、

- __フォローする側のUserからみたRelationshipを`active_relationship`__
- __フォローされる側のUserからみたRelationshipを`passive_relationship`__

としてみます。

参照元のモデルは先ほどもやった`class_name: "Relationship"`と指定すれば良いだけなので、

~~~ruby:例
has_many :active_relationships, class_name: "Relationship"
has_many :passive_relationships, class_name: "Relationship"
~~~
となります。

ですがこれだとまだ、 __親モデルの外部キーがなんなのか__ という情報が足りません。
`active_relationship`で言うと「 __フォローする側のUserからみた__ 」と言う情報が足りていない、と言うことになります。

そこで親モデルの外部キー指定するオプションとして、`foreign_key`と言うのがあります。

#### foreign_key
`:foreign_key` - _参照先のテーブルの外部キーのカラム名を指定できる_
これにそれぞれ`following_id`, `follower_id`、つまり __親の`primary_key`__ を指定してあげれば、「 __フォローする側のUserからみた__ 」と言う情報も取得することができます。

よって、最終的なUserモデルの最終的なアソシエーションの記述は以下のようになります。

~~~ruby:app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  has_many :tweets
  has_many :favorites
  has_many :favorite_tweets, through: :favorites, source: :tweet

  # ====================自分がフォローしているユーザーとの関連 ===================================
  #フォローする側のUserから見て、フォローされる側のUserを(中間テーブルを介して)集める。なので親はfollowing_id(フォローする側)
  has_many :active_relationships, class_name: "Relationship", foreign_key: :following_id
  # 中間テーブルを介して「follower」モデルのUser(フォローされた側)を集めることを「followings」と定義
  has_many :followings, through: :active_relationships, source: :follower
  # ========================================================================================

  # ====================自分がフォローされるユーザーとの関連 ===================================
  #フォローされる側のUserから見て、フォローしてくる側のUserを(中間テーブルを介して)集める。なので親はfollower_id(フォローされる側)
  has_many :passive_relationships, class_name: "Relationship", foreign_key: :follower_id
  # 中間テーブルを介して「following」モデルのUser(フォローする側)を集めることを「followers」と定義
  has_many :followers, through: :passive_relationships, source: :following
  # =======================================================================================

  def followed_by?(user)
    # 今自分(引数のuser)がフォローしようとしているユーザー(レシーバー)がフォローされているユーザー(つまりpassive)の中から、引数に渡されたユーザー(自分)がいるかどうかを調べる
    passive_relationships.find_by(following_id: user.id).present?
  end
end
~~~

先ほどまでの話にプラスして、`has many through`で(中間テーブルを介して)`followings`の時は`follower`を、`followers`の時は`following`を集める記述をしています。
もちろん`source`でモデルの参照元を指定しています。
followingsで集めるUserはフォローされる側(follower)。逆も然りです。

以下の画像は、これまでの自己結合アソシエーションの記述をまとめたものです。


<img width="1041" alt="qiita26.png" src="https://qiita-image-store.s3.amazonaws.com/0/208038/6eb61dff-c98b-9074-a112-18a823479336.png">


favoriteと同じく、 __Userがfollow済みかどうか判定__ したいため、 __followed_by?__ と言うメソッドも追加しています。

これでUserモデルのアソシエーションの記述も完了です。

残るはcontroller、viewを実装です！

### コントローラ、ビューの実装
これで本当にチュートリアルは最後です。頑張っていきましょう！！！

#### ビューページの実装

(最後にある完成版のソースコードに、もう少し綺麗にしたviewファイルの奴も置いてあるので、もしよろしければそちらも参考ください。)

#### フォローボタンの実装

~~~erb:app/views/tweets/index.html.erb
<h1>Tweets#index</h1>
<p>Find me in app/views/tweets/index.html.erb</p>
<% @tweets.each do |tweet| %>
  <hr>
  <p><span>email: </span><%=link_to tweet.user.email, user_path(tweet.user.id) %></p>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
  <% if user_signed_in? %>
    <% if tweet.favorited_by?(current_user) %> <!-- ログインしているユーザーがファボしたかどうかで分岐 -->
        <p><span>お気に入り解除: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :delete %></p>
    <% else %>
        <p><span>お気に入り登録: </span><%=link_to tweet.favorites.count, tweet_favorites_path(tweet.id), method: :post %></p>
    <% end %>

    <!-- ここを追加 -->
    <% if current_user != tweet.user %>
        <% if tweet.user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(tweet.user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(tweet.user.id), method: :post %></p>
        <% end %>
    <% end %>
    <!-- ここまで -->

  <% else %>
    <p><span>お気に入り数: </span><%= tweet.favorites.count %></p>
  <% end %>
<% end %>
~~~


~~~erb:app/views/users/index.html.erb
<h1>Users#index</h1>
<p>Find me in app/views/users/index.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%= link_to user.email, user_path(user.id) %></p>
    <!-- ここを追加 -->
    <% if current_user != user %>
        <% if user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(user.id), method: :post %></p>
        <% end %>
    <% end %>
    <!-- ここまで -->
<% end %>
~~~


自分をフォローする自体は避けたいので、`if current_user != user`で自分をのぞいた人たちのみボタンを表示するような実装にしています。あとはほとんどfavoriteボタンと同じですね。

tweetsのshowページは省略します。理屈がわかっていれば絶対にできるはずです、やってみましょう！
次にコントローラです。

#### relationshipsコントローラの実装

~~~ruby:app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
  def create
    follow = current_user.active_relationships.build(follower_id: params[:user_id])
    follow.save
    redirect_to users_path
  end

  def destroy
    follow = current_user.active_relationships.find_by(follower_id: params[:user_id])
    follow.destroy
    redirect_to root_path
  end
end
~~~

favoriteの時と同じく`current_user.active_relationships.build`とすることで「`following_id: current_user.id`」を代入しながらインスタンスを作成することができます。

最後に、フォロー、フォロワー一覧を実装しましょう。

#### usersコントローラの実装

~~~ruby:app/controllers/users_controller.rb
class UsersController < ApplicationController
  before_action :authenticate_user!
  def index
    @users = User.all
  end

  def show
    @user = User.find(params[:id])
    @tweets = @user.tweets
    @favorite_tweets = @user.favorite_tweets
  end

# ==============追加================
  def follows
    user = User.find(params[:id])
    @users = user.followings
  end

  def followers
    user = User.find(params[:id])
    @users = user.followers
  end
# ==============追加================
end

~~~

#### ビューの実装

follows, followersのビューは作っていなかったので、このタイミングで新規作成してください。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/208038/cf506491-6838-a858-364e-dcc81f8278ba.png)

~~~erb:app/views/users/follows.html.erb
<h1>Users#follows</h1>
<p>Find me in app/views/users/follows.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%= link_to user.email, user_path(user.id) %></p>
    <% if current_user != user %>
        <% if user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(user.id), method: :post %></p>
        <% end %>
    <% end %>
<% end %>
~~~


~~~erb:app/views/users/followers.html.erb
<h1>Users#followers</h1>
<p>Find me in app/views/users/followers.html.erb</p>

<% @users.each do |user| %>
  <hr>
  <p><span>email: </span><%= link_to user.email, user_path(user.id) %></p>
    <% if current_user != user %>
        <% if user.followed_by?(current_user) %>
            <p><%=link_to "フォロー済み", user_relationships_path(user.id), method: :delete %></p>
        <% else %>
            <p><%=link_to "フォローする", user_relationships_path(user.id), method: :post %></p>
        <% end %>
    <% end %>
<% end %>
~~~

~~~erb:app/views/users/show.html.erb
<h1>Users#show</h1>
<p>Find me in app/views/users/show.html.erb</p>
<hr>
<p><span>email: </span><%= @user.email %></p>

<!-- 追加 -->
<p><%=link_to "フォロー", follows_user_path(@user.id) %></p>
<p><%=link_to "フォロワー", followers_user_path(@user.id) %></p>
<% if current_user != @user %>
    <% if @user.followed_by?(current_user) %>
        <p><%=link_to "フォロー済み", user_relationships_path(@user.id), method: :delete %></p>
    <% else %>
        <p><%=link_to "フォローする", user_relationships_path(@user.id), method: :post %></p>
    <% end %>
<% end %>
<!--ここまで -->

<% @tweets.each do |tweet| %>
  <hr>
  <p><span>ツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>

<% @favorite_tweets.each do |tweet| %>
  <hr>
  <p><span>ファボツイート内容: </span><%=link_to tweet.body, tweet_path(tweet.id) %></p>
<% end %>
~~~

以上でフォロー機能の実装は完了です！！本当におつかれさまでした！！！！！！

http://localhost:3000

## 自己結合多対多まとめ

- __User同士の関連付け__ なので、名前の重複が色々起きてしまう。そこは`class_name`オプションを使って名称変更しよう。その時の`foreign_key`の指定も忘れずに。
- __followする側が持つのはfollower達__ 。つまり __foreign_keyはfollowing_id__ なのに注意(逆も然り)
- あとは普通の多対多とだいたい同じ

## 完成版のソースコード

| 目次        |
|:-----------|
| [アソシエーションとは](#アソシエーションとは)|
| [データベース設計とは](#データベース設計とは少し難しい話です)|
| [UserとTweetの実装(１対多)](#ログイン機能とツイート機能をer図を使って設計しよう)|
| [Favoriteの実装(多対多)](#お気に入り機能をer図を使って設計しよう)|
| [Follow機能の実装(自己結合多対多)](#フォローフォロワー機能をer図を使って設計しよう)|
| [ __完成版のソースコード__ ](#完成版のソースコード)   ←今ココ|

そのまま実装 - https://github.com/mormorbump/association_tutorial
リファクタリング済み(ヴァリデーション、パーシャル化済み) - https://github.com/mormorbump/association_tutorial_refactor
heroku - https://rails-association-tutorial.herokuapp.com
丁寧すぎるherokuへのデプロイの方法はこちら - [【初心者向け】railsアプリをherokuを使って確実にデプロイする方法【決定版】](https://qiita.com/kazukimatsumoto/items/a0daa7281a3948701c39)


## ポリモーフィック関連(おまけ)

※コメント機能を実装してある前提で進めます。

いつか書きます。