rails いいね機能を非同期処理で実装してみた

# いいね機能と非同期処理

SNSのコピーサイトを作成するにあたり、いいね機能の実装をすることとしました。
私自身初学者なので拙い文章になるかと思いますが、同じくrailsを勉強し始めの人にもわかるように一つ一つの作業を詳しく説明していきます。

＜開発環境＞
1. ruby 2.6.3
2. Rails 5.1.6
3. AWS Cloud9
4. GitHub
6. sqlite3(develop環境)


## 開発環境を整える

＊既にuserモデル、postモデルを作成済みであることを前提に進めさせてもらいます。

### jqueryのインストール


```ruby:Gemfile
gem 'jquery-rails'
```
gemfileにこちらを記載した後bundle install

そして、assets/javascripts/application.jsの中に以下を記載する。

```ruby:application.js
//= require jquery
//= require rails-ujs
```
他の方の記事を参考にするとこちらのコードを記載する順番も大事とのことなので順番は変えないようにします。
これでjQueryを使用する準備ができました。

### modelの作成


```
$ rails g model Like user_id:string post_id:string
```
今回はuser_idとpost_idカラムを作るようにしっかり記載してますがuser:references post:referencesでも同じカラムが作られるのでお好きな方でどうぞ。


LikeテーブルはUserテーブルとPostテーブルの中間に位置するもので、いいねをするにはまず投稿の情報を取得してその後にログイン中のユーザーがその投稿に対していいねをすることができ、また、その逆にユーザーがどの投稿にいいねをしているかという情報も取得できるという流れになります。

この流れは続く作業にも繋がってくるので頭の片隅に置いておいてくれれば幸いです。

### テーブルごとのアソシエーション

```ruby:user.rb
has_many :likes, dependent: :destroy
```
userは多くのlikeを持っていてユーザーが消去されるとそのuserのlikeも同時に消去されることを意味しています。

```ruby:post.rb
 has_many :likes, dependent: :destroy
```

先ほどと同様にpostは多くのlikeを持っていてその投稿が消去されるとlikeも同時に消去されます。

```ruby:like.rb
 belongs_to :user
 belongs_to :post
```
likeは一人のuserが一つのpostに対してできることを意味しています。

これでテーブル周りの作業は完了です。

### likes controllerの作成

```
$ rails g controller likes create destroy
```

いいね機能はいいねをする、いいねを取り消すという二つのアクションしか必要ないのでcreateとdestroyメソッドを作成します。

次はコントローラの中身を整理していきます。

```ruby:likes_controller.rb
before_action :set_controller
  
  def create
    @like = current_user.likes.new(post_id: params[:post_id])
    @like.save
    @like_count = Like.where(post_id: params[:post_id]).count
  end

  def destroy
    @like = Like.find_by(user_id: current_user.id, post_id: params[:post_id])
    @like.destroy
    @like_count = Like.where(post_id: params[:post_id]).count
  end
  
  def set_controller
    @post = Post.find(params[:post_id])
  end
```

こちらは新規投稿、投稿削除の流れと基本的に同じです。
@like_countでその投稿に対していいねされた数を取得しています。

また、post controllerにもいいね数が表示されるようにしておきます。

```ruby:posts_controller.rb
def index
.
.
@posts = Post.all.order(created_at: :desc)
@like_count = Like.where(post_id: params[:id]).count
.
.
```

###ルーティング設定

ルーティングに関しては他の記事でも様々な設定方法がされていますが自分は以下のようにしました。

```ruby:routes.rb
 post '/like/:post_id', to: 'likes#create', as: 'like'
 delete '/like/:post_id', to: 'likes#destroy', as: 'unlike'
```


``` resources :likes, only: [:create, :destroy]```
でもいいじゃんと思っていたのですが、この設定にするとlikeテーブルからわざわざlikeのidを取得する必要が出てきてしまいますが、必要なのはログイン中のユーザーのidと投稿のidですので上記のように設定しました。

私自身一度試してみたのですがidがnilになってしまうエラーが解消できなかったので上記のルーティングに設定し直しました。

もし詳しい方がいたらコメントで教えていただければ嬉しいです、、、


### viewの設定

あとは見た目の部分を整えていきます。
今回は投稿一覧ページと投稿詳細ページにいいねボタンを設置しましたが、ここでは投稿一覧への設置の方法のみ説明をしていきます。

```ruby:index.html.erb
<% @posts.each do |post| %>
 <%= link_to post_path(post.id) do %>

.
.
.

    <div class= "buttons_<%= post.id %>">
     <% if current_user.likes.find_by(post_id: post.id) %>
       <%= link_to unlike_path(post.id), method: :delete, remote: true do %>
         <div class= "buttons_">❤<%= @like_count %></div>
       <% end %>
     <% else %>
       <%= link_to like_path(post.id), method: :post, remote: true do %>
        <div class= "buttons_">♡️<%= @like_count %></div>
       <% end %>
     <% end %>
    </div>
.
.
.
 <% end %>
<% end %>
```

link_toの中にある remote: trueによってjsのerbに飛ばすことができます。
次にjs用のerbを用意します。

```ruby:create.js.erb
$(".buttons_<%= @post.id %>").html("<%= j(render partial: 'likes/like') %>");
```

```ruby:destroy.js.erb
$(".buttons_<%= @post.id %>").html("<%= j(render partial: 'likes/like') %>");
```

たったこれだけのコードで非同期処理を実装することができました！

最後にcssでいいねボタンを整えていきます。
[[Rails]いいね機能の非同期での実装!!!](https://qiita.com/hayabusa3703/items/2b916e652a1dc85bb6e3)
こちらのページを参考にさせて頂きました。（自分はセンスがないので脱帽です、、、）

``` ruby:custom.css
.buttons_{
    background: linear-gradient(to right, #feb400, #FFE300);
    line-height: 42px;
    color: #6d3701;
    font-size: 14px;
    display: inline-block;
    padding: 0 18px;
    border-radius: 100px;
    font-weight: bold;
}
```

![ezgif.com-video-to-gif.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/618929/43dd983e-cb1d-0cd0-9f50-a5257060677e.gif)



これでいいね機能、非同期処理の完成です！