[Rails]Ajaxを用いて非同期でフォロー機能の実装

## 実装すること
Ajaxを用いて非同期通信を行い、フォロー機能を書いていきます。
Ajaxについては下記リンク先で説明しています。
[Rails]Ajaxを用いて非同期でコメント機能の実装：https://qiita.com/yuto_1014/items/c7d6213139a48833e21a

## ER図
 Userテーブル同士で「多対多」の関係を作ります。なぜなら、フォローしているユーザーもフォロワーもユーザになるからです。そこでUserテーブル同士の中間テーブルとしてRelationshipテーブルを作成します。
<img width="612" alt="フォローER図.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/539971/e66ac1ac-2cda-cac5-cc77-bc4a52349a65.png">

## ページ設計
ユーザー詳細ページ(user/show.html.erb)でフォローができる。

## モデルの作成
Userモデルは作成した前提で進めていきます。
作成手順は下記リンク先で説明しております。
[Rails]Ajaxを用いて非同期で投稿機能といいね機能の実装https://qiita.com/yuto_1014/items/78d8b52d33a12ec33448
それではRelationshipモデルを作成していきます。

```
$ rails g model Relationship follower_id:integer following_id:integer
$ rails db:migrate
```

## マイグレーションファイルの追記
`t.index [:follower_id, :following_id], unique: true`で、一度フォローしたユーザーを２度フォローしてしまわないようにするための一意の設定をしています。

```20200109085109_create_relationships_.rb

class CreateRelationships < ActiveRecord::Migration[5.2]
  def change
    create_table :relationships do |t|
      t.integer :follower_id
      t.integer :following_id

      t.index [:follower_id, :following_id], unique: true ←追記

      t.timestamps
    end
  end
end


```

## アソシエーションの確認
### Userモデル
・**『following_relationships』**
following_relationshipsモデルを架空で作成しています。
`class_name: "Relationship"`でRelationshipモデルの、`foreign_key: "follower_id"` で、follower_idを参考に、following_relationshipsモデルへアクセスするようにしています。
フォロワーに関しても同じです。
・**『following』**
`through: :following_relationships`で、中間テーブルにfollowing_relationshipsテーブルを指定しています。その結果、user.following と打つだけで、userが中間テーブルfollowing_relationships を取得し、その1つ1つのfollowing_idから、「フォローしているUser達」を取得できるようになります。
・**『def following?』**
following_relationshipsテーブルのfollowing_idにuserのidが存在するか確認しています。
・**『def follow?』**
フォローするときのメソッドを作成しています。
このメソッドが呼び出されたときには、following_idにuser.idを代入します。
・**『def unfollow?』**
フォローを外すときのメソッドを作成しています。
このメソッドが呼び出されたときには、following_idのuser.idを削除します。

```app/models/user.rb
class User < ApplicationRecord

   has_many :following_relationships, foreign_key: "follower_id", class_name: "Relationship",  dependent: :destroy
   has_many :following, through: :following_relationships
   has_many :follower_relationships, foreign_key: "following_id", class_name: "Relationship", dependent: :destroy
   has_many :followers, through: :follower_relationships

  #フォローしているかを確認するメソッド
  def following?(user)
    following_relationships.find_by(following_id: user.id)
  end

  #フォローするときのメソッド
  def follow(user)
    following_relationships.create!(following_id: user.id)
  end

  #フォローを外すときのメソッド
  def unfollow(user)
    following_relationships.find_by(following_id: user.id).destroy
  end

```

### Relationshipsモデル
`class_name: "User"` と補足設定することで、followerクラス、followingクラスという存在しないクラスを参照することを防ぎ、Userクラスであることを明示しています。

```app/models/relationship.rb
    #自分をフォローしているユーザー
	belongs_to :follower, class_name: "User"
	#自分がフォローしているユーザー
	belongs_to :following, class_name: "User"
	#バリデーション
	validates :follower_id, presence: true
	validates :following_id, presence: true
```

## コントローラーの作成

```
$ rails g controller relationships
```

## ルーティングの作成
フォローユーザーとフォロワーを取れるようにしています。
following_user GET /users/:id/following(.:format) users#following
followers_user GET /users/:id/followers(.:format) users#followers

```config/routes.rb
Rails.application.routes.draw do

    resources :users do
      member do
     	  get :following, :followers
      end
    end
    resources :relationships, only: [:create, :destroy]

end
```


## コントローラーの編集
### users_controller.rb
フォローユーザー一覧とフォロワー一覧を表示するためのアクションを作成します。
`@user.following`と`@user.followers`では、Userモデルのメソッドを呼び出しています。

```app/controllers/users_controller.rb
class UsersController < ApplicationController

    def following
		#@userがフォローしているユーザー
	    @user  = User.find(params[:id])
	    @users = @user.following
	    render 'show_follow'
	end

	def followers
		#@userをフォローしているユーザー
	    @user  = User.find(params[:id])
	    @users = @user.followers
	    render 'show_follower'
	end
end
```

## ralationships_controller.rb
フォローする・フォロー解除するためのアクションを作成します。
`follow`と`unfollow`では、Userモデルのメソッドを呼び出しています。

```app/controllers/relationships_controller.rb
class RelationshipsController < ApplicationController

    def create
	  @user = User.find(params[:following_id])
	  current_user.follow(@user)
	end

	def destroy
	  @user = User.find(params[:id])
	  current_user.unfollow(@user)
	end
end
```

## ビューの編集
### ユーザー詳細ページ(users/show.html.erb)
・`followers.count`でフォロワーの人数、`following.count`でフォローユーザーの人数を取っています。
・フォローボタンは、パーシャルにしています。

```app/views/users/show.html

  <div class="follower">
	  <%= link_to followers_users_user_path(user.id) do %>
		  <h5 style="color: black;">フォロワー<%= user.followers.count %>人</h5>
	  <% end %>
  </div>
  <div class="follow">
	  <%= link_to following_users_user_path(user.id) do %>
				<h5 style="color: black;">フォロー<%= user.following.count %>人</h5>
	  <% end %>
  </div>
  <div>
	  <%= render "follow_form" %>
  </div>

```

### フォローボタン(users/_follow_form.html.erb)
・`if user_signed_in? && @user != current_user`では、ユーザーがログインしていて且つユーザー詳細ページ(users/show.html.erb)に表示されているユーザーが、ログインユーザーでなければ、フォローボタンを表示するようにしています。
・`if current_user.following?(@user)`で、ログインユーザーが@userをすでにフォローしているかどうかをUserモデルのメソッドを呼び出して確認しています。フォローしているか否かで呼び出されるパーシャルが変わってきます。

```app/views/users/_follow_form.html
<!-- フォローボタン(MYPAGEのユーザーがcurrent_userでなければ表示------------------------------------------------------>
<% if user_signed_in? && @user != current_user %>
  <div id="follow_form">
    <% if current_user.following?(@user) %>
      <%= render "unfollow" %>
    <% else %>
      <%= render "follow" %>
    <% end %>
  </div>
<% end %>
```
###まだフォローしていなかった場合
**users/_follow.html.erb**
・`remote: true`で、relationshipsコントローラーのcreateアクションに飛んだ後、create.js.erbに飛ぶようにしています。
・`hidden_field_tag :following_id, @user.id`で、following_idに@user.idを代入しています。

```app/views/users/_follow.html

<!-- フォローボタン ------------------------------------------------------------------>
<%= form_for(current_user, url: relationships_path, method: :post, remote: true) do |f| %>
  <%= hidden_field_tag :following_id, @user.id %>
  <%= f.submit "フォローする", class: "btn btn-outline-secondary" %>
<% end %>
```

**relationships/create.js.erb**
relationshipsコントローラのcreateアクションの処理後、
users/_follow_form.htmlの`id=follow_form`をターゲットに、unfollowパーシャルを差し替えています。

```app/views/relationships/create.js
$("#follow_form").html("<%= j(render("users/users/unfollow")) %>");
```


### 既にフォローしていた場合
**users/_unfollow.html.erb**
`remote: true`で、relationshipsコントローラーのdestroyアクションに飛んだ後、destroy.js.erbに飛ぶようにしています。

```app/views/users/_unfollow.html

<!-- フォロー解除ボタン ------------------------------------------------------------------>
<%= form_for(current_user, url: relationship_path(@user), method: :delete, remote: true) do |f| %>
  <%= f.submit "フォロー解除", class: "btn btn-outline-secondary" %>
<% end %>
```

**relationships/destroy.js.erb**
relationshipsコントローラのdestroyアクションの処理後、
users/_follow_form.htmlの`id=follow_form`をターゲットに、followパーシャルを差し替えています。

```app/views/relationships/destroy.js
$("#follow_form").html("<%= j(render("users/users/follow")) %>");
```

## 最後に
最後までご覧いただきありがとうございます。
初学者ですので間違っていたり、分かりづらい部分もあるかと思います。
何かお気付きの点がございましたら、お気軽にコメントいただけると幸いです。
Ajax関連でいいねとコメントの非同期も実装しています。
↓
[Rails]Ajaxを用いて非同期で投稿機能といいね機能の実装
https://qiita.com/yuto_1014/items/78d8b52d33a12ec33448
[Rails]Ajaxを用いて非同期でコメント機能の実装
https://qiita.com/yuto_1014/items/c7d6213139a48833e21a

## 参考
・railsでフォロー機能をつける。
https://qiita.com/kitaokeita/items/59b625e0c43a62f5fe6a
・Railsでフォロー機能を作る方法
https://qiita.com/MitsuguSueyoshi/items/e41e2ff37f143db81897
・フォロー機能、完成版
https://qiita.com/Kaisyou/items/86869db6345c9cc1413f