お気に入り機能の実装

#目次
[1.はじめに](#1-はじめに)
[2.いいねボタンの導入](#2-いいねボタンの導入)
[3.同期通信で実装する](#3-同期通信で実装する)
[4.非同期通信で実装する](#4-非同期通信で実装する)
[5.おわりに](#5-おわりに)

#1. はじめに
今回は同期通信で実装した後、それをもとに非同期通信に切り替えていきます。まずは必要なものを導入しましょう。

#2. いいねボタンの導入
`font-awesome-rails`を導入しましょう。もし文字をクリックしてお気に入り機能を実装する場合であれば[3.同期通信で実装する](#3-同期通信で実装する)から実装していただいて問題ないです。以下のようにアイコンをクリックしてお気に入りをつけたり外したりしたい場合はこちらを参考にしてください。
![52a7c16510cbb3fd2f8095bae614ab1e.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/953064/a163576e-b82b-952a-aed0-b567229fdec7.gif)





###1. GEMの導入
```ruby:gemfile
gem 'font-awesome-rails'
```
＊導入したら必ず`bundle install`してください。

###2. CSSファイルに記述を追加する
```css:app/assets/stylesheets/application.css
*= require font-awesome
```
これで使う準備はOKです。

###3. 使用したいビューファイルに記述を追加する
```ruby:ビューファイル
<i class="fa fa-アイコン名" aria-hidden="true"></i>
```
どんなアイコンを使用するかはご自由に！！
[ここをクリック！][link-1]から探してね！！
[link-1]:https://fontawesome.com/v4.7.0/icons/

#3. 同期通信で実装する
**アプリケーション作成、ユーザー登録、投稿機能ができている体で進みます。そこまでは自力で実装してください。**

###1. お気に入り機能に必要なものを揃える
まずは必要なもの...そうですコントローラーとモデルですね！
どんな名前でもいいので作成してください。今回この記事では`like`という名前で実装してます。

###2. ルーティングの設定
今回は投稿機能に関連する機能のため、ネスト構造で設定します。今回はお気に入りをつけて外すだけなので`create`と`destroy`のみ記述してます。

```ruby:routes.rb
 Rails.application.routes.draw do
  #(省略)
  resources :posts do
    resources :likes, only: [:create, :destroy]
  end
 end
```

###3. モデルの設定
まず、マイグレーションファイルには`user_id`と`post_id`が保存できるように設定してマイグレートしておいてください。
今回１人のユーザーはお気に入りを何回もすることができます。また、投稿された物に対してもたくさんお気に入りすることができます。
そのため以下のようにアソシエーションを設定します。

```ruby:app/models/like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post
  
end
```

```ruby:app/models/user.rb
#以下を追加する
has_many :likes
```

```ruby:app/models/post.rb
#以下を追加する
has_many :likes
```

###4. コントローラーの設定
まずはお気に入りを登録する`createアクション`の設定です。
今回は投稿したpostに対して行いたいためまずはどのpostなのかを取得する必要があります。その後、そのpostに対してお気に入りを新しく作り、保存ができたらトップページに戻るように設定します。

お気に入りの削除は今の考え方で、保存ではなく削除したら...の場合で記述しましょう。
以下のようになります。

```ruby:likes_controller.rb
class LikesController < ApplicationController
　before_action :set_post
  def create
    @like = Like.new(user_id: current_user.id, post_id: params[:post_id])
    @like.save
    redirect_to root_path
  end

  def destroy
    @like = current_user.likes.find_by(post_id: @post.id)
    @like.destroy
    redirect_to root_path
  end

private
  
  def set_post
    @post = Post.find(params[:post_id])
  end
end
```

###5. ビューファイルの設定
お気に入り機能だけを別に切り出したいので今回私は部分テンプレートに記述しました。そのため、元々の記述の部分は以下のようになります。

```erb:views/posts/index.html.erb
#(省略)
<div class="likes_heart" id="post_<%= post.id %>">
   <%= render "likes/like", post: post %> 
</div>
```

部分テンプレートでは、以下の条件をつけました。
・ログインしていないとお気に入りできない
・１投稿に対して１回しかお気に入りができない（既にお気に入りに登録していたら削除するボタンになる）

```erb:likes/_like.html.erb
<% if user_signed_in? %>
  <% if post.liked_by?(current_user) %>
    <%=link_to post_like_path(post_id: post.id, id: post.id), method: :delete do%>
      <i class="fa fa-heart" aria-hidden="true" id="heart"></i>
      <%= post.likes.count %>
    <% end %>
  <% else %>
    <%=link_to post_likes_path(post), method: :post do%>
      <i class="fa fa-heart" aria-hidden="true" id="heart"></i>
      <%= post.likes.count %>
    <% end %>
  <% end%>
<% end %>
```

また、今回liked_byというメソッドを使用します。whereメソッドを使用し、likesテーブルに「user_id」が存在しているかどうか検索をかけています。(存在すれば削除、存在しなければお気に入り追加、と振り分ける)

```ruby:app/models/post.rb
#省略
def liked_by?(user)
    likes.where(user_id: user.id).exists?
end
```

同期通信はこれで終わり！！！実際に動かしてみてください！！

#4. 非同期通信で実装する
ここが本当に難しい...と感じました！もう少し！頑張りましょう！！

###1. コントローラーの修正
非同期通信に変更するのでトップページに遷移させる記述を削除しましょう。
以下のようになります。

```ruby:likes_controller.rb
class LikesController < ApplicationController
  before_action :set_post

  def create
    @like = Like.new(user_id: current_user.id, post_id: params[:post_id])
    @like.save
  end

  def destroy
    @like = current_user.likes.find_by(post_id: @post.id)
    @like.destroy
  end
  
  private
  
  def set_post
    @post = Post.find(params[:post_id])
  end

end
```

###2. ビューファイルの設定
「remote: true」を付与します。付与することで、パラメーターがHTML形式ではなくJS形式で送られるようになります。
今回の場合は「remote: true」をlink_toメソッドに付与することで、likes_controller.rbのcreateアクション後は、views/likes/create.js.erbが呼び出されるようになります。

```erb:views/likes/_like.html.erb
<% if user_signed_in? %>
  <% if post.liked_by?(current_user) %>
    <%=link_to post_like_path(post_id: post.id, id: post.id), method: :delete,remote: true do%>
      <i class="fa fa-heart" aria-hidden="true"></i>
      <%= post.likes.count %>
    <% end %>
  <% else %>
    <%#link_to post_likes_path(post), method: :post do%>
    <%= link_to post_likes_path(post), method: :post, remote: true do%>
      <i class="fa fa-heart" aria-hidden="true"></i>
      <%= post.likes.count %>
    <% end %>
  <% end%>
<% end %>
```

###3. js.erbファイルの作成
今回は２つ用意しましょう。
記述は以下です。

```erb:views/likes/create.js.erb
document.getElementById('post_<%= @post.id %>').innerHTML = '<%= j(render 'like' ,post: @post) %>'
```

```erb:views/likes/destroy.js.erb
document.getElementById('post_<%= @post.id %>').innerHTML = '<%= j(render 'like' ,post: @post) %>'
```

詳しくみていきましょう。
まずはこちらです。

```
document.getElementById('post_<%= @post.id %>')
```
書き換えたいビューのIDを取得しています。
IDに関しては、[3.同期通信で実装する](#3-同期通信で実装する)の**5.ビューファイルの設定**で設定してますので確認してください。

```
'<%= j(render 'like' ,post: @post) %>'
```
部分テンプレートの内容に置き換えることを表してます。
今回使用した部分テンプレートは`_like.html.erb`なのでこのような書き方です。部分テンプレートに保存/削除した内容を渡さないといけないので、部分テンプレート内で使用している変数`post: @post`を記述してます。


#5. おわりに
普通にJSファイルを作成して行うやり方もあると思います。皆さんがしっくりくるやり方で実装してみてください！！