Railsでフォロー機能を実装する

# なぜこの記事を書いたのか
Railsでのフォロー機能について説明している記事はたくさんあります。

しかし、「自分**が**フォローしているユーザーの一覧ページ」と「自分**を**フォロしているユーザーの一覧ページ」までの実装手順を説明している記事は少なく感じました。

この「フォロー一覧」と「フォロワー一覧」ですが、**Relationshipテーブルのカラムをよく考えて命名**しておかないと、ややこしくなることがあります。

```following```, ```follower```, ```follow```, ```followed```など様々な命名方法が記事によって混在していますが、一番混乱しにくい命名方法で解説されている記事が少なかったため、改めて記事を作成することにしました。

まず結論から言います。

この記事では、**フォローする側は```following```**、**フォローされる側は```follower```**としておくとよく覚えておいてください。

普通の感覚だと、『フォローする側が```follower```じゃないの？』と感じるかもしれません。長くなるので詳しい説明は省きますが、仮にその命名方法を取った場合、あるユーザーの「フォロワー一覧」を表示する際に参照するカラムが、```follower```カラムではなく、もう一方のカラムになってしまうため、色々と不都合が起きます。

現時点でこの意味が分からなくても全く問題ないので、ここではとにかく**フォローする側は```following```**、**フォローされる側は```follower```**だということを認識しておいてください。

# 前提
* MVCを理解している
* 基本的なアソシエーションを理解している
* Userモデルが作成してある

# 流れ
1. Relationshipモデルを作る
2. アソシエーションを組む
3. ルーティングを組む
4. コントローラーでアクションを定義
5. ビューを編集


# Relationshipモデルを作る
フォロー機能では、フォローする側もフォローされる側もUserであるため、Userテーブルのレコード同士で「多対多」の関係を作る必要があります。

これを実現するために、フォローするユーザーとフォローされるユーザーの情報を保存しておく中間テーブルを作る必要があります。

例えば、ユーザーAがユーザーBをフォローした場合は、以下のようにして情報を記録します。

**Userテーブル**

|id|name|
|:-:|:-:|
|1|ユーザーA|
|2|ユーザーB|

**Relationshipテーブル**

|id|following|follower|
|:-:|:-:|:-:|
||1|2|


以下をターミナルに打ち込んで、実際に中間テーブルであるRelationshipテーブルを作ってみましょう。

```terminal
$ rails g model relationship following_id:integer follower_id:integer
$ rails db:migrate
```
これでRelationshipという中間テーブルができました！
このテーブルの存在意義は、以下の２つの情報を保存しておくことです。

* フォローするユーザーが誰なのかを示す```following```カラム
* フォローされるユーザーが誰なのかを示す```follower```カラム

# アソシエーション
今作成したRelationshipモデルにある```following```と```follower```という情報は、どちらも親であるUserテーブルから情報を参照する必要があります。（フォローする側もされる側もどちらもUserであるため。）

FollowingモデルとFollowerモデルを擬似的に作成することで、その参照先をうまく分けることができます。

Relationshipモデルを以下のように変更してください。

```app/models/relationship.rb
class Relationship < ApplicationRecord
    belongs_to :following, class_name: "User"
    belongs_to :follower, class_name: 'User'
end
```

class_nameにUserと記述することで、それぞれの参照先がUserモデルであることを示しています。

次にUserモデル側にアソシエーションを記述しましょう。

まずは以下のコードをまとめて追加してください。

```app/models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable

  #以下を挿入

  has_many :following, class_name: "Relationship", foreign_key: "following_id", dependent: :destroy
  has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy

  has_many :following_user, through: :following, source: :follower
  has_many :follower_user, through: :follower, source: :following

  def follow(user_id)
    following.create(follower_id: user_id)
  end

  def unfollow(user_id)
    following.find_by(follower_id: user_id).destroy
  end

  def following?(user_id)
    following_user.include?(user_id)
  end
end
```
順番に説明していきます。
まずは以下の部分について。

```ruby
  has_many :following, class_name: "Relationship", foreign_key: "following_id", dependent: :destroy
  has_many :follower, class_name: "Relationship", foreign_key: "follower_id", dependent: :destroy
```

１つ目の```has_many```では、Relationshipモデルのfollowing_idにuser_idを格納する作業を行っています。その際、foreign_keyで```folowings_id```と明示的に格納したいカラムを指定しています。

２つ目の```has_many```では、Relationshipモデルのfollower_idにuser_idを格納する作業を行っています。その際、foreign_keyで```follower_id```と明示的に格納したいカラムを指定しています。

次に、以下の部分について。

```ruby
  has_many :following_user, through: :following, source: :follower
  has_many :follower_user, through: :follower, source: :following
```

ここでは、自分がフォローしているユーザーと自分をフォローしているユーザーを簡単に取得するためにthroughを使った関連付けを行っているだけです。

こうすることで、例えば```@user.following_user```などと記述することで自分がフォローしたユーザーの情報が取得できるようになります。

最後にこの部分について

```ruby
  def follow(user_id)
    following.create(follower_id: user_id)
  end

  def unfollow(user_id)
    following.find_by(follower_id: user_id).destroy
  end

  def following?(user_id)
    following_user.include?(user_id)
  end
end
```

ここではフォローするための関数，フォローを外すための関数，ユーザーを既にフォローしているかを調べる関数をそれぞれ作成しています。

これでフォロー機能で一番難しいアソシエーションの記述が終わりました！
ここまできたら、あとは基本的なMVCを理解していれば簡単です！

# ルーティング
フォローしたり、フォローを外すためのアクションを定義する準備として、まずは以下のようにしてルーティングを行いましょう。

```config/routes.rb
  post 'follow/:id' => 'relationships#follow', as: 'follow'
  delete 'unfollow/:id' => 'relationships#unfollow', as: 'unfollow'
```

続いて、「フォロー一覧」と「フォロワー一覧」のページを作るために、以下のルーティングも行っておきます。

```config/routes.rb
  resources :users, only: [:show] do
    # 以下を挿入
    get :following, :follower, on: :member
  end
```

# コントローラー
続いてrelationshipsコントローラーでのアクションの定義です。

まずはrelationshipsコントローラーを、ターミナルに以下のコードを打ち込んで作成しましょう。

```terminal
$ rails g controller Relationships new
```

以下のようにして、ユーザーをフォローできるfollowアクションと、ユーザーへのフォローを外すことができるunfollowアクションを定義します。

```app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController
    def follow
      current_user.follow(params[:id])
      redirect_back(fallback_location: root_path)
    end
  
    def unfollow
      current_user.unfollow(params[:id])
      redirect_back(fallback_location: root_path)
    end
end
```

続いて、usersコントローラーで「フォロー一覧」と「フォロワー一覧」を表示するために必要なアクションも以下のようにして記述しておきます。

```app/controllers/users_controller.rb
class UsersController < ApplicationController
    def show
        @user = User.find(params[:id])
    end

    # 以下を挿入

    def following
        @user  = User.find(params[:id])
    end
  
    def follower
        @user  = User.find(params[:id])
    end
end
```

あとはビューに記述していくだけです！！

# ビュー

user#showページにフォローボタンやフォローを外すボタンを記述する時の例を載せておきます。

あくまで一例なので、自分のプロダクトに合うよう、適宜書き換えてください。

```html:app/views/users/show.html.erb
<% if user_signed_in? %>
  <% unless @user == current_user %>
    <% if current_user.following?(@user) %>
      <%= link_to unfollow_path(@user.id), method: :delete, :style=>"color:#E0245E;" do %>
        <i class="fas fa-heart"></i>
      <% end %>
    <% else %>
      <%= link_to follow_path(@user.id), method: :post do %>
        <i class="far fa-heart"></i>
      <% end %>
    <% end %>
  <% end %>
<% end %>
```

以下は、「フォロー一覧」と「フォロワー一覧」に飛ぶためのリンクコードの例です。

```html:app/views/users/show.html.erb
<a href="<%= following_user_path(@user) %>">
  <%= @user.following.count %> following
</a>
<a href="<%= follower_user_path(@user) %>">
  <%= @user.follower.count %> followers
</a>
```

また、「フォロー一覧」と「フォロワー一覧」では以下を参考にして、適宜自分の表示させたい情報を記述していきましょう。

```html:app/views/users/following.html.erb
<% @user.following_user.each do |user| %>
  <% user.name %>
<% end %>
```

```html:app/views/users/follower.html.erb
<% @user.follower_user.each do |user| %>
  <% user.name %>
<% end %>
```

これで完成です！お疲れ様でした！