Railsでいいね機能を実装。Ajaxを使い非同期対応。で

今回は、Railsでツイッターのようにポストに対して、いいねができ、押すとそこの見た目が変わる機能を作成します。
こちらの記事[railsとjsを使ったお手軽「いいね♡機能」](https://qiita.com/YuitoSato/items/94913d6a349a530b2ea2)を参考にして作ったのですが、ajaxが完全に動かず、リロードしないとうまく動作しないエラーで長い時間苦労したので今回はそこを克服できたのをも書ければなと思います。

仕様を簡単にまとめるとこんな感じ。
- UserがPostに対していいねできる。
- 中間テーブルとしてLikeモデルを作成し、両者のIDを取得。
- ajaxを使って非同期処理。
- いいねされた数も表示される。
- ログインしているユーザーしかいいねできない。

このような感じでしょうか。

では早速行きましょう！

## Likeモデルの作成

ターミナルで以下のコマンドを実行しましょう。

```bash:terminal
rails g model Like user:references post:references

rails g controller Likes create destroy

rails g migration AddNumcountToPost likes_count:integer
```
上二つはいいとして、一番下はポストに対してどれだけいいねが付いているかを保存するカラムを追加します。

## モデルの編集。

Postモデル、作成したLikeモデルを以下のように編集します。

```ruby:post.rb
class Post < ApplicationRecord
  belongs_to :user
  has_many :likes, dependent: :destroy

  def like_user(user_id)
   likes.find_by(user_id: user_id)
  end

end

```

dependent: :destroyはpostが削除されたらこれに関連したlikeも削除されるよという意味です。

like_user関数は、そのユーザーが持っているlikeモデルを探します。

```ruby:like.rb
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post, counter_cache: :likes_count
end
```
counter_cache: :likes_countがカウントしてくれます。

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

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

  def create
    @like = Like.create(user_id: current_user.id, post_id: params[:post_id])
    @likes = Like.where(post_id: params[:post_id])
    @post.reload
  end

  def destroy
    like = Like.find_by(user_id: current_user.id, post_id: params[:post_id])
    like.destroy
    @likes = Like.where(post_id: params[:post_id])
    @post.reload
  end

  private

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

end

```
とします。


## Routeの変更

```ruby:routes.rb
    resources :posts do
      resources :likes, only: [:create, :destroy]
    end
```

## Viewファイル

```erb:_like.html.erb
<% if user_signed_in? %>

  <% unless post.like_user(current_user.id).blank? %>
    <%= link_to post_like_path(post_id: post.id ,id: post.likes[0].id), method: :delete, remote: true do %>
      <div class="vertical_like">
        <i class="material-icons red-text">favorite</i>
        <span class="red-text">
          <%= post.likes_count %>
        </span>
      </div>
    <% end %>
  <% else %>
    <%= link_to post_likes_path(post.id), method: :post, remote: true do %>
      <div class="vertical_like">
        <i class="material-icons grey-text text-darken-2">favorite_border</i>
        <span class="grey-text text-darken-2">
          <%= post.likes_count %>
        </span>
      </div>
    <% end %>
  <% end %>

<% else %>

  <% if post.likes_count > 0 %>
    <div class="vertical_like">
      <i class="material-icons red-text">favorite</i>
      <span class="red-text">
        <%= post.likes_count %>
      </span>
    </div>
  <% else %>
    <div class="vertical_like">
      <i class="material-icons grey-text text-darken-2">favorite_border</i>
      <span class="grey-text text-darken-2">
        <%= post.likes_count %>
      </span>
    </div>
  <% end %>

<% end %>
```
とします。Iconは[materlize](http://materializecss.com/icons.html)のものを使用しております。

```erb:create.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

```

```erb:destroy.js.erb
$('#likes_buttons_<%= @post.id %>').html("<%= j(render partial: 'likes/like', locals: {post: @post}) %>");

```

としましょう。

## いいね!ボタンを表示されたいところに記述。

トップページにpost一覧を表示しているところに以下を記述しましょう。
前提として、各Postの内容を変数 post で回していることを想定します。

```erb:index.html.erb
<div id="likes_buttons_<%= post.id %>">
 <%= render partial: 'likes/like', locals: { post: post, likes: @likes} %>
</div>
```