[Ajax]フォロー機能を非同期にした

# はじめに
本記事では、フォロー機能を非同期通信にした方法を記述します。

以前にいいね機能について、非同期通信にした記事も投稿しておりますので、
ご参照ください。

https://qiita.com/mkato1013/items/9ee39a7675371aa585ce

## 前提
フォロー機能が同期通信できているものとします。

# コード
早速ですが、該当箇所のコードを記載します。

## コントローラー
showページにフォローボタンやフォロワーを確認できるボタンがあります。
後述のモデルに合わせて`followings`や``followers`メソッドを作成します。

また、`users`と`followers`などに分けている方もいらっしゃいましたが、
私は、`followings`や``followers`に分けた方がわかりやすいと思ったため、こうしています。

```users_controller.rb
class UsersController < ApplicationController

  def show
    @user = User.find(params[:id])
    @foods = @user.foods.order("created_at DESC")
  end

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

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

省略

end
```
`relationships`テーブルは、userとuserの関係を示す、`中間テーブル`になります。
また、その関係を作る(create)、壊す(destroy)の２つが必要になります。

```relationships_controller.rb
class RelationshipsController < ApplicationController
  before_action :authenticate_user!

  def create
    @user = User.find(params[:user_id])
    following = Relationship.create(follower_id: params[:user_id], following_id: current_user.id)
  end

  def destroy
    @user = User.find(params[:user_id])
    following = Relationship.find_by(follower_id: params[:user_id], following_id: current_user.id)
    following.destroy
  end
end
```

## モデル
同期通信と特に変わりはありません。

```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 :foods, dependent: :destroy
  has_many :likes, dependent: :destroy
  has_many :liked_foods, through: :likes, source: :food
  
  has_many :relationships, foreign_key: :following_id
  has_many :followings, through: :relationships, source: :follower
  has_many :reverse_of_relationships, class_name: 'Relationship', foreign_key: :follower_id
  has_many :followers, through: :reverse_of_relationships, source: :following

  has_one_attached :icon

  has_many :comments
  
  def already_liked?(food)
    self.likes.exists?(food_id: food)
  end

  def is_followed_by?(user)
    reverse_of_relationships.find_by(following_id: user.id).present?
  end

  
end
```
ここも同期通信と特に変わりはありません。

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

## ビュー
`show.html.erb`
記述が長いので、部分テンプレートです。
ちなみに、クラス名を
`class="mypage-follow-contents-<%= @user.id %>"`
にしないと、誰をフォローするのかわからないままですので、
エラーが出続けることになります。

```erb
<div class="mypage-intro-topright">
  <div class="mypage-follow-contents-<%= @user.id %>">
    <%= render partial:"relationships/relationship", lacals: {user: @user} %>
  </div>
</div>
```

`followers.html.erb`フォロワーの一覧を確認できるページ。ここはあまり関係ないと思います。

```erb
<div class="followers-contents">
  <div class="row d-flex justify-content-center">
    <div class="col-10 mt-5">
      <h1>FOLLOWERS</h1>
        <h2 class="followers-contents-title">Nickname</h2>
          <% @users.each do |user| %>
            <%= link_to user_path(user) do %>
              <%= user.nickname %>
            <% end %>
              <% if user != current_user %>
                <% if user.is_followed_by?(current_user) %>
                  <%=link_to user_relationships_path(user), method: :delete do %>
                    <button type="button" class="btn btn btn-danger">フォロー解除</button>
                  <% end %>
                <% else %>
                  <%=link_to user_relationships_path(user), method: :post do %>
                    <button type="button" class="btn btn btn-primary">フォロー</button>
                  <% end %>
                <% end %>
              <% end %>
          <% end %>
    </div>
  </div>
</div>
```

`followings.html.erb`フォローしている人を確認できるページ。ここはあまり関係ないと思います。

```erb
<div class="followings-contents">
  <div class="row d-flex justify-content-center">
    <div class="col-10 mt-5">
      <h1>FOLLOWING</h1>
        <h2 class="followings-contents-title">Nickname</h2>
          <% @users.each do |user| %>
            <%= link_to user_path(user) do %>
              <%= user.nickname %>
            <% end %>
              <% if user != current_user %>
                <% if user.is_followed_by?(current_user) %>
                  <%=link_to user_relationships_path(user), method: :delete do %>
                    <button type="button" class="btn btn btn-danger">フォロー解除</button>
                  <% end %>
                <% else %>
                  <%=link_to user_relationships_path(user), method: :post do %>
                    <button type="button" class="btn btn btn-primary">フォロー</button>
                  <% end %>
                <% end %>
              <% end %>
          <% end %>
    </div>
  </div>
</div>
```
`_relationship.html.erb`
`show.html.erb`にあった部分テンプレートです。

`remote: true`これが特に大事です。
というか非同期通信のスタートがこれなんではないかというぐらい重要です。
これにより、非同期通信をOKするものになり、リクエストが`js形式`になります。

そのため、後ほど出てきますが、
ファイルは`js.erbファイル`ということになります。

```erb
<div class="mypage-followcount-btn">
  <%= link_to followings_user_path(@user) do %>
    <button class="btn btn-mypage-follow">フォロー
      <i class="fas fa-angle-right fa-position-right">
        <%= @user.followings.count %>
      </i>
    </button>
  <% end %>
  <%= link_to followers_user_path(@user) do %>
    <button class="btn btn-mypage-follow">フォロワー
      <i class="fas fa-angle-right fa-position-right">
        <%= @user.followers.count %>
      </i>
    </button>
  <% end %>
  <% if user_signed_in? && @user == current_user %>
    <div class="mypage-introduction-edit">
      <%= link_to edit_user_path(current_user) do %>
        <button class="btn btn-mypage-introduction-edit">プロフィール編集
          <i class="fas fa-user-edit"></i>
        </button>
      <% end %>
    </div>
  <% else %>
    <%# <% unless @user == current_user %>
    <div class="mypage-follow-btn">
      <% if @user.is_followed_by?(current_user) %>
        <%= link_to user_relationships_path(@user), method: :delete, remote: true do %>
          <button class="btn btn-follow-delete">フォロー中</button>
        <% end %>
      <% else %>
        <%= link_to user_relationships_path(@user), method: :post, remote: true do %>
          <button class="btn btn-follow-create">フォローする</button>
        <% end %>
      <% end %>
    </div>
  <% end %>
</div>
```
`create.js.erb`フォロー
`j`については、以下にも記載したので省略します。

https://qiita.com/mkato1013/items/9ee39a7675371aa585ce

```erb
$('.mypage-follow-contents-<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: {user: @user}) %>")
```
部分テンプレートにより、
前述の以下の部分に進みます。

```erb
<% if @user.is_followed_by?(current_user) %>
  <%= link_to user_relationships_path(@user), method: :delete, remote: true do %>
    <button class="btn btn-follow-delete">フォロー中</button>
  <% end %>
<% else %>
   <%= link_to user_relationships_path(@user), method: :post, remote: true do %>
    <button class="btn btn-follow-create">フォローする</button>
   <% end %>
<% end %>
```

`destroy.js.erb`フォロー解除
create.js.erbと同じです。

```erb
$('.mypage-follow-contents-<%= @user.id %>').html("<%= j(render partial: 'relationships/relationship', locals: {user: @user}) %>")
```

以上です。
いかがでしょうか。

# 終わりに
非同期通信、終わってみれば「簡単」だと思いますが、
できるまでは難しかったなというのが、印象でした。

アプリケーションにおいて非同期化してしまった方が良いものは全て非同期化した方が良さそうですね。

以下、参考サイトです。
[[Rails]Ajaxを用いて非同期でフォロー機能の実装](https://qiita.com/yuto_1014/items/8d508b84fd0c2316ba01)
[【Rails】Ajaxを用いた非同期フォロー機能の実装](https://qiita.com/matsubishi5/items/3a70689b42dd98b4d99b)
[Railsで実装したフォロー機能の非同期化がうまくいかない。](https://teratail.com/questions/325022)
[Ruby on Rails チュートリアル](https://railstutorial.jp/chapters/following_users?version=5.0)

明日も頑張ります！！