railsとjsを使ったお手軽「いいね♡機能」

#はじめに

プログラミング初心者の皆さんこんにちは！（こちらの記事はプログラミング初心者を対象に書いています。）

今回はjavascriptをあまり書いたことのない初心者の方でも簡単につくれるrailsとjavascriptを使った「いいね（like）機能」を実装していこうと思います。
基本的なrailsの説明は省いています。

今回はrailsのひとつ完成したアプリケーションがあるとしてそこに「いいね機能」を付け足すという流れで進めていきます。
今回は例として、ツイッターのようなSNSアプリに他人のツイートにいいねできるような機能を追加していこうと思います。
なのでtweet、userモデルとtweetsコントローラーがある前提で話を進めていきます。
一つのツイートに対して一人のユーザーがいいねできるのは一回までで、ツイッターのように灰色のハートを押すといいね(createアクション）、既にいいねされている赤のハートを押すといいねを取り消すこと(destroyアクション)ができます。

#likesテーブルを作成しよう
ターミナルでlikesテーブルを作成しましょう。カラムはinteger型でuser_idとtweet_idを追加しておきましょう。
モデルファイルに以下を記述。

```ruby:models/like.rb
class Like < ActiveRecord::Base
  belongs_to :tweet, counter_cache: :likes_count
  belongs_to :user
end
```

counter_cahce: :likes_countはリレーションされているlikeの数の値をリレーション先のlikes_countというカラムの値に入れますよって意味です。なのでlikes_countカラムをtweetsテーブルに追加しましょう。

#tweetsテーブルにlikes_countカラムを追加しよう
tweetsテーブルにinteger型のlikes_countというカラムを追加しましょう。
さらに同モデルファイルに以下の記述を追加しましょう。

```ruby:tweet.rb
class Tweet < ActiveRecord::Base
  has_many :likes, dependent: :destroy
  def like_user(user_id)
   likes.find_by(user_id: user_id)
  end
end
```

like_userメソッドは後に取り扱いますが、端的にいうとそのユーザーが既にツイートにいいねしているかどうか確認するメソッドです。


#ルーティングを記述しよう
tweetsとlikesはネスト構造をとっています。resourcesを使いながら記述しましょう。


```ruby:routes.rb
Rails.application.routes.draw do
  resources :tweets do
    resources :likes, only: [:create, :destroy]
  end
end
```

#アクションを定義しましょう
まずはlikes controllerを作成しましょう。作成したらそのファイルに以下を記述しましょう

```ruby:likes_controller.rb
class LikesController < ApplicationController
  def create
    @like = Like.create(user_id: current_user.id, tweet_id: params[:tweet_id])
    @likes = Like.where(tweet_id: params[:tweet_id])
    @tweets = Tweet.all
  end

  def destroy
    like = Like.find_by(user_id: current_user.id, tweet_id: params[:tweet_id])
    like.destroy
    @likes = Like.where(tweet_id: params[:tweet_id])
    @tweets = Tweet.all
  end
end

```

いいね（like）を増やすcreateアクションもdestroyアクションも同じで増やすたり消したりするlikeとそのあとの合計いいね数を表示するための@likes、とその後、tweetの一覧ページを表示するための@tweetsが定義されています。（ここでは説明を簡潔にしたいので記述を最低限におさえています。）


#部分テンプレートを作ろう
今回はいいねボタンを部分テンプレートを使って作ります。
ユーザーがサインインしていない状態では押しても何も反応しないハートを表示します。ユーザーがサインインしているときは先ほど定義したlike_userメソッドを使いながら、そのユーザーがツイートにいいねしているかどうか判断しながら、表示を切り分けます。
ちなみに先ほど、likesのモデルファイルでcounter_cacheの記述を書いているので、tweet.like_countとするだけでそのツイートに結びつくlike数が表示されます。
どちらのアクションのパスもremote: trueの記述をしてajaxを使いながらアクションを実行します。
icon_red_heard.svgがいいね後の赤いハート、
icon_heart.svgがいいね前の灰色のハートととしてます。

```html:_like.html.erb
<% if user_signed_in? %>
  <% if tweet.like_user(current_user.id) %>
    <%= button_to tweet_like_path(like, tweet_id: tweet.id), method: :delete, id: "like-button", remote: true do %>
      <%= image_tag("icon_red_heart.svg") %>
      <span>
        <%= tweet.like_count %>
      </span>
    <% end %>
  <% else %>
    <%= button_to tweet_likes_path(tweet), id: "like-button", remote: true do %>
      <%= image_tag("icon_heart.svg") %>
      <span>
        <%= tweet.like_count %>
      </span>
    <% end %>
  <% end %>
<% else %>
  <% = image_tag("icon_heart.svg") %>
    <span>
      <%= tweet.like_count %>
    </span>
<% end %>
```

#JSファイルを作ろう
いよいよjsを書く時がやって来ました。
ajaxを使ったページ遷移をせずにいいねを増やしたり消したりするjavascriptファイルを作りましょう。

```js:views/likes/create.js.erb
$("#like-buttons").html("<%= j(render partial: 'like', locals: { tweets: @tweets, likes: @likes, like: @like}) %>")

```

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

```

以上です。なんとjsを書くのはたったこれだけなんですね。なので今回のいいね機能はrailsの基本を押さえている方なら、多分すぐ出来てしまいます。簡単かつ普通に動くので他にもやり方は色々ありますが、このやり方は特におすすめです。