Railsで「いいね！」機能を作る - ③「いいね！」を解除できるようにする

### ここまでの前提／今回のゴール
さて、初学者向けのテキストを噛み砕いて解いてみるシリーズ、第３段となりました。
ここまでは、Twitterのようなアプリで、投稿に「いいね！」機能をつけることを目標に、以下のような手順でコードを書いてきました。

[Railsで「いいね！」機能を作る - ①アソシエーションに別名をつける](https://qiita.com/tanutanu/items/2a27f1e503c4cbf30928)
[Railsで「いいね！」機能を作る - ②「いいね！」のcreateアクション](https://qiita.com/tanutanu/items/bff95ce3a91dab1eb2c6)

今回は、これらをさらに一歩進めて「いいね！」した投稿の「いいね！」を解除する機能を作ろうと思います。

[![Image from Gyazo](https://i.gyazo.com/4672dd7fbedb43ac8cc44f2a48e94a17.png)](https://gyazo.com/4672dd7fbedb43ac8cc44f2a48e94a17)

ユーザーが投稿した内容について、ボタンを押すごとに「いいね！」と「いいね！解除」が切り替わり「いいね！」とそれをやめることができる仕様にしたいと思います。

### View
今回はビューから実装をスタートします。
ビューはこんな感じで作成しました。

```erb:views/posts/_post.erb
<!-- postsのインスタンスは、パーシャルに渡されている前提です。 -->
<% posts.each do |post| %>
  <div>
    <!-- 投稿者名・投稿日時・投稿内容 -->
    <div>
      <%= post.user.name %> posted at <%= post.created_at %>
    </div>
    <div>
      <p><%= post.content %></p>
    </div>

    <!-- いいね！ボタン -->
    <div>
      <!-- すでに「いいね！」されている時の表示 -->
      <% if current_user.favorites.include?(post) %>     
        <%= form_with(model: @like, url: like_path(id: post.id), local: true, method: :delete) do |f| %>
          <%= f.hidden_field :post_id, value: post.id %>
          <%= f.submit 'いいね！解除' %>
        <% end %>
      <% else %>

　　　　　<!-- まだ「いいね！」していない時の表示 -->
        <%= form_with(model: @like, url: likes_path, local: true) do |f| %>
          <%= f.hidden_field :post_id, value: post.id %>
          <%= f.submit 'いいね！' %>
        <% end %>
      <% end %>      
    </div>
  </div>
<% end %>
```

一つ目のポイントですが、`form_with(model: @like, url: ...)`の部分では、`model: Like`ではなく、`model: @like`を使わないとダメでした。  

なぜかなと思い[apiドキュメント](https://api.rubyonrails.org/v6.0/classes/ActionView/Helpers/FormHelper.html#method-i-form_with)を調べてみたものの、ここだけは解決せず。  

ただ、[こちらの記事](https://qiita.com/hakusai_it/items/234d10c97c380294c285)によると「隠しフィールドでデータを送りたいときは@like」ということだそうなので、今回はここまでの理解でませることにします。  

`form_with(model: @like) do |f|`の形で、Railsは自動でリクエストの送信先URLを推測してくれますが、今回は、デフォルトのURLではエラーが出るので、`url:`オプションでリクエストの送信先を指定します。

また、`url: like_path(id: post.id)`でクリックした投稿のidがリクエストの`params`に含まれていることにも注目してください。

最後に、`local: true`でajaxではない通信を指定し、`method: :delete`でDELETEメソッドでRailsにリクエストを送信することを記載しています。

### Controller
コントローラーの記載はこんな感じです。

```Ruby
class LikesController < ApplicationController
  
  def create
    current_user.like_this(clicked_post)
    flash[:success] = '投稿に「いいね！」しました。'
    redirect_back(fallback_location: root_path)
    # 前回の記事からelse以下の記述は削除
  end

  # ↓前回の記事からここを追記！
  def destroy
    current_user.likes.find_by(post_id: params[:post_id]).destroy
    flash[:danger] = '「いいね！」を解除しました。'
    redirect_back(fallback_location: root_path)
  end
  
  private
  
  def clicked_post
    Post.find(params[:post_id])
  end
end
```

まず、destroyアクションは、`current_user.likes.find_by(post_id: params[:micropost_id]).destroy`で現在のユーザーの「いいね！」した投稿の中にクリックした投稿が含まれているか確認します。

クリックした投稿のidは`params[:post_id]`の形で取り出すことができます。（さっき`url: like_path(id: post.id)`の形で送りましたね！）
そして、投稿を見つけたら`destroy`をします。

上記で、`destroy`アクションが実装できたので、`create`アクションの記述も少し書き換えます。さらに不要になったコードは消しておきます。

以上で、いいね！／いいね解除機能の実装は完了です:relaxed:
`@like`の構造だけ説明がつかなかったのが残念ですが、これで自信を持って説明できそうです:relieved:

余裕があれば、これをさらに応用して**ユーザーのフォロー／フォロー解除**機能も実装し、詳しく読み解いていきたいです。

ここまで読んでくだっさった皆様、ありがとうございました＾＾

### 追記
2020.09.29 一部メソッドをリファクタリングしました

### その他
応用編で、こんなものも作りました。

[RailsでAjaxで「いいね！」機能を実装する。](https://qiita.com/tanutanu/items/b435af08aff706e01f2a)