Railsの非同期いいねの方法


今回はRailsの非同期いいねの方法について紹介します。

参考記事
https://zenn.dev/odentravel/books/e69a157daeecb3/viewer/b583e5 
https://zenn.dev/odentravel/books/e69a157daeecb3/viewer/6d1551

### 同期いいねの作成
参考記事では、postとuserの紐付けになっていますが、自分はrecruitmentとuserの紐付けになります。
最初にmodelを作成して、dbマイグレイトを行います。
```ruby
rails g model Favorite user_id:integer recruitment_id:integer
rails db:migrate
```
次にアソーシエーションの設定をします。

```ruby
#models/user.rb

class User < ApplicationRecord
#〜省略〜
  has_many :favorites, dependent: :destroy
end

```

```ruby
#models/recruitment.rb

class Recruitment < ApplicationRecord
#〜省略〜
  has_many :favorites, dependent: :destroy

  def favorited_by?(user)
    favorites.where(user_id: user.id).exists?
  end
end
```
favorited_by?では、誰がいいねしたのかを判別します。

```ruby
#models/favorite.rb
class Favorite < ApplicationRecord
  belongs_to :user
end
```
これで設定が完了しました。
その次にコントローラーを作成します。

```
$ rails g controller Favorites
```
そして、コントローラーを記述します。
内容としては、recruitmentの記事を特定して、ログインユーザーがいいねしたのを保存 or 削除するという処理です。
この時点では、リダイレクトの処理を記述します。
```ruby
#FavoritesController.rb
class FavoritesController < ApplicationController
def create
  @recruitment = Recruitment.find(params[:recruitment_id])
  favorite = current_user.favorites.new(recruitment_id: @recruitment.id)
  favorite.save
  redirect_to recruitment_path(recruitment)
end

def destroy
  @recruitment = Recruitment.find(params[:recruitment_id])
  favorite = current_user.favorites.find_by(recruitment_id: @recruitment.id)
  favorite.destroy
  redirect_to recruitment_path(recruitment)
end
end

```

そしてViewを作成します。
この時、いいね作成は、 method: :post　にします。
（自分は間違って、method: :recruitment にしてしまい、エラーが出てしまいました。）
```Ruby
#show.html.erb
          <td>
			<p class="caption"><%= recruitment.locate %>
    <% if @recruitment.favorited_by?(current_user) %>
    <%= link_to recruitment_favorites_path(@recruitment), method: :delete, class: "favorite_btn" do %>
      ♥<%= @recruitment.favorites.count %> いいね
    <% end %>
    <% else %>
      <%= link_to recruitment_favorites_path(@recruitment), method: :post, class: "favorite_btn" do %>
      ♡<%= @recruitment.favorites.count %> いいね
      <% end %>
    <% end %>
    </p>	</td>
```
これで同期いいねの完成です。

### 非同期いいねの作成

その次にいよいよ非同期いいねの作成に入ります。

最初にlink_toにremote: trueを追記します。これで非同期通信になります。
```Ruby
#show.html.erb
          <td>
					<p class="caption"><%= recruitment.locate %>
    <% if @recruitment.favorited_by?(current_user) %>
    <%= link_to recruitment_favorites_path(@recruitment), method: :delete, remote: true, class: "favorite_btn" do %>
      ♥<%= @recruitment.favorites.count %> いいね
    <% end %>
    <% else %>
      <%= link_to recruitment_favorites_path(@recruitment), method: :post,remote: true,  class: "favorite_btn" do %>
      ♡<%= @recruitment.favorites.count %> いいね
      <% end %>
    <% end %>
    </p>	</td>

```

その次に、部分テンプレート化します。

```Ruby
#favorite/_favorite.html.erb
          <td>
					<p class="caption"><%= recruitment.locate %>
    <% if recruitment.favorited_by?(current_user) %>
    <%= link_to recruitment_favorites_path(recruitment), method: :delete, remote: true, class: "favorite_btn" do %>
      ♥<%= recruitment.favorites.count %> いいね
    <% end %>
    <% else %>
      <%= link_to recruitment_favorites_path(recruitment), method: :post,remote: true,  class: "favorite_btn" do %>
      ♡<%= recruitment.favorites.count %> いいね
      <% end %>
    <% end %>
    </p>
					</td>

```

そして、表示部分を下記のように書き換えます。

```Ruby
#favorite/show.html
			<div id="favorite_btn_<%= @recruitment.id %>">
  <%= render 'favorites/favorite', recruitment: @recruitment %>
			</div>

```

次に非同期処理を実現するためのjsファイルを作成します。

```Ruby
#favorite/create.js.erb
$('#favorite_btn_<%= @recruitment.id %>').html("<%= j(render partial: 'favorites/favorite', locals: {recruitment: @recruitment}) %>");

```

```Ruby
#favorite/destroy.js.erb
$('#favorite_btn_<%= @recruitment.id %>').html("<%= j(render partial: 'favorites/favorite', locals: {recruitment: @recruitment}) %>");

```

処理の意味としては、ボタンが押された時、favorites/favoriteを再レンダーするという意味になります。

最後リダイレクトの削除と@をつけます。

```
class FavoritesController < ApplicationController
def create
  @recruitment = Recruitment.find(params[:recruitment_id])
  favorite = current_user.favorites.new(recruitment_id: @recruitment.id)
  favorite.save
end

def destroy
  @recruitment = Recruitment.find(params[:recruitment_id])
  favorite = current_user.favorites.find_by(recruitment_id: @recruitment.id)
  favorite.destroy
end
end


```

これで非同期いいね機能の完成です。