[Rails]フォロー機能実装について

Railsでのフォロー機能実装に手間取ったので、
自分が理解した範囲で手順を書いていく。

※プログラミング初心者のため、内容に不備や間違っている箇所があるかもしれません。
自分用のメモとして残していますが、もし不備等あればご指摘いただければと思います。

###開発環境###
Ruby 2.6.3
Rails 5.2.4
###前提機能###
ログイン機能（devise）
###実装の流れ###
① 中間テーブルを作成する

② Userモデル同士を参照するアソシエーションを記述する

③ follow、unfollowメソッドを定義する

④ フォローボタンを作成、コントローラを記述する

##① 中間テーブルを作成する
「フォローするユーザ」と「フォローされるユーザ」を取得する際、
どちらも同じUserモデルを参照するため中間テーブルが必要になる。
中間テーブルについてはこちらの記事が大変わかりやすく、参考にさせていただいた。
https://qiita.com/morikuma709/items/1e389ddcdfc1102ef3f4

まず、Relationshipモデルを作成

```
$ rails g model Relationship
```

```create_relationship.rb
class CreateRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :relationships do |t|
      t.references :follower, foreign_key: true
      t.references :followed, foreign_key: true

      t.timestamps
      t.index [:follower_id, :followed_id], unique: true
    end
  end
end
```

カラムには「follower_id(フォローするユーザ)」と「followed_id(フォローされるユーザ)」を設定する。
どちらもUserモデルにて外部キーとして使用する(後述)のでreferencesで定義。
migrateを忘れずに。 

```
 $ rails db:migrate 
```

##② モデルの関連付け###



```relationship.rb
class Relationship < ApplicationRecord
  
  belongs_to :follwer, class_name: "User"
  belongs_to :follwed, class_name: "User"
  
end
```
ここの書き方は単純。
先ほど設定した「follower_id」と「followed_id」の値からUsersテーブルのレコードを参照する。
ただ、「class_name: 'モデル名'」で参照するモデルを指定しなければいけないことに注意。

続いてUserモデルを記述していく。
ここが個人的に一番混乱した。

```user.rb
  
  # フォローするユーザ
  has_many :active_relationships, class_name: "Relationship",
                                  foreign_key: "follower_id",
                                  dependent: :destroy
  # 自分がフォローしているユーザ
  has_many :followed_users, through: :active_relationships,
                            source: :followed
  # フォローされるユーザ
  has_many :passive_relationships, class_name: "Relationship",
                                   foreign_key: "followed_id",
                                   dependent: :destroy
  # 自分をフォローしているユーザ
  has_many :following_users, through: :passive_relationships,
                             source: :follower

```

has_manyに続くモデル名は新たに作成したもの。もちろんモデル自体は存在しない。

・フォローするユーザとフォローされるユーザを取得
　今回はそれぞれ、「active_relationships」と「passive_relationships」とした。

　　→active_relationshipsの場合
　　　参照するモデルはRelationshipモデル（class_name: "Relationship"）
　　　さらに、値を参照するカラムは「follower_id」(foreign_key: "follower_id")

・自分がフォローしているユーザと自分をフォローしているユーザ
　こちらは「followed_users」と「following_users」とした。
  
  　　→followed_usersの場合
　　　　active_relationshipsユーザーからフォローされているユーザ
　　　　active_relationshipsを参照（through: :passive_relationships）
　　　　取得する値は「followed_id」(source: :followed)

##③ メソッドを定義する ##
Relationshipsコントローラのアクション内で使用するメソッドを定義する。
場所はUserモデル。

```user.rb
  def follow(user_id)
    active_relationships.create(followed_id: user_id)
  end
  
  def unfollow(user_id)
    active_relationships.find_by(followed_id: user_id).destroy
  end
  
  def following?(user)
    followed_users.include?(user)
  end
```

##④ フォローボタン作成、コントローラを記述##
続いてコントローラ

```relationships_controller.rb
class RelationshipsController < ApplicationController
  
  def create
    current_user.follow(params[:user_id])
    redirect_to request.referer
  end
  
  def destroy
    current_user.unfollow(params[:user_id])
    redirect_to request.referer
  end
  
end
```

viewファイルにフォローボタンを作成する

```users/show.html.erb
  <% if current_user.following?(@user) %>
    <%= link_to 'フォロー中', user_relationship_path(@user), method: :delete, class:"btn" %>
  <% else %>
    <%= link_to 'フォローする', user_relationships_path(@user), method: :post, class:"btn" %>
  <% end %>  
```

@userにはparams[:id]が格納されている。
current_user.following?(@user)で@userがfollowed_usersに含まれているかを判断する。

以上でフォロー機能の実装は完了。
時間がある時に一覧ページ作成の流れも追記しようと思う。

####自分がたどり着いた考え方まとめ####

active_relationships  = フォローするユーザ
followed_users        = active_relationshipsにフォローされているユーザ
passive_relationships = フォローされるユーザ
following_users       = active_relationshipsをフォローしている

初めてフォロー機能に触れた時、「する」「される」の関係がわからなくなり、アソシエーションの中身がぐちゃぐちゃになっていた。解決するのにだいぶ時間を使ってしまった。
モデルの名義やそもそものやり方は人それぞれなので、自分が理解できるやり方を見つけるのが一番だと感じた。