いいね機能の実装について

## はじめに

今回は、オリジナルアプリに実装した「いいね機能」について
振り返りながら記事を書きたいと思います。

オリジナルアプリでいいね機能は2箇所実装しました。
1.投稿に対するいいね
2.コメントに対するいいね
この2つです。
別テーブルで作成しました。

今回は、コメントに対するいいね機能の記事を書きたいと思います。
なお、ユーザー管理機能、投稿機能、コメント機能は実装済みです。

1.テーブル設計
2.モデル作成(テーブル作成、バリデーション、アソシエーション)
3.ルーティング設定
4.コントローラー設定
5.ビューの設定

## バージョン
・Ruby 2.6.5
・Rails 6.0.0

## テーブル設計

・likesテーブル

| Column    | Type       | Options                        |
| --------- | ---------- | ------------------------------ |
| user      | references | null: false, foreign_key: true |
| post      | references | null: false, foreign_key: true |
| comment   | references | null: false, foreign_key: true |

### Association
- belongs_to :user
- belongs_to :post
- belongs_to :comment

user、post、commentは紐づいているので、references型でオプションでforeign_key: true 
をつけています。
commentにいいねしたい場合、投稿も紐づいているためpostカラムも必要になります。
どのユーザーがどの投稿のどのコメントにいいねをしたのかわかるようにしてます。
アソシエーションはユーザー、投稿、コメントは1に対してlikeは多の関係になります。

## モデルの作成

テーブル設計ができましたら、次にモデルを作成します。

```
% rails g model like
```
モデルを作成したら、マイグレーションファイルに記述します。

2021xxxxxxx_create_likes.rb

```ruby
class CreateLikes < ActiveRecord::Migration[6.0]
  def change
    create_table :likes do |t|
      t.references :user,    null: false, foreign_key: true
      t.references :post,    null: false, foreign_key: true
      t.references :comment,    null: false, foreign_key: true
      t.timestamps
    end
  end
end
```

先ほどのテーブルのカラムを記述したら、

```
% rails db:migrate
```

マイグレーションを行いテーブルを作成完了です！！

次にモデルにアソシエーションとバリデーションの記述を行います。

models/like.rb

```ruby
class Like < ApplicationRecord
  belongs_to :user
  belongs_to :post
  belongs_to :comment

  validates_uniqueness_of :comment_id, scope: :user_id
end
```

ここでのバリデーションは、1つのコメントに対して1度しかいいねできないように
組んでいます。

ここにコードは載せませんが、User、Post、Commentのモデルに
has_many:likesの記述を行っています。

## ルーティングの設定

routes.rb

```ruby
Rails.application.routes.draw do
  devise_for :users

  root to: 'posts#index'
  resources :users, only: [:show, :edit, :update, :destroy]
  resources :posts do
    resources :comments, only: :create do
      resources :likes, only: [:create, :destroy]
    end
  end
end

```

ここで、ルーティングをネストさせます。
まず、postsコントローラー(親)→commentsコントローラー(子)の関係性があります。
そこに、commentsコントローラー(親)→likesコントローラー(子)の関係性でネストさせます。
ネストをさせる理由は、アソシエーション先のレコードのidをparamsに追加してコントローラーに送るためです。
今回だと、いいねに結びつくコメント投稿のidをparamsに追加します。

これで、ターミナルでルーティングを確認する。

```
% rails routes
```
[![Image from Gyazo](https://i.gyazo.com/60ce83a071ac7c13c498bb0d04cc98e9.png)](https://gyazo.com/60ce83a071ac7c13c498bb0d04cc98e9)

## コントローラーの設定

まずは、コントローラーを作成します。

```
% rails g controller likes
```

次にコントローラーにコードを記述します。

controllers/likes_controller.rb

```ruby
class LikesController < ApplicationController

  def create
    post = Post.find(params[:post_id])
    comment = Comment.find(params[:comment_id])
    @like = Like.create(user_id: current_user.id, post_id: post.id, comment_id: comment.id)
    redirect_to post_path(comment.post)
  end

  def destroy
    post = Post.find(params[:post_id])
    comment = Comment.find(params[:comment_id])
    Like.find_by(user_id: current_user.id, post_id: post.id, comment_id: comment.id).destroy
    redirect_to post_path(comment.post)
  end
end

```
ここでは、いいねの保存と削除のアクションを記述しています。

### createアクション
```ruby
post = Post.find(params[:post_id])
comment = Comment.find(params[:comment_id])
```

いいねされたコメントに紐づく投稿を変数postに格納する。
いいねされたコメントのidとPostテーブルのidが一致するものをfindで見つけて変数postに格納。


```ruby
@like = Like.create(user_id: current_user.id, post_id: post.id, comment_id: comment.id)
```
こちらでいいねを保存する処理を記述します。

### destroyアクション

```ruby
def destroy
    post = Post.find(params[:post_id])
    comment = Comment.find(params[:comment_id])
    Like.find_by(user_id: current_user.id, post_id: post.id, comment_id: comment.id).destroy
    redirect_to post_path(comment.post)
  end
```
find_byメソッドを使用して、それぞれのidが一致したいいねを取り消す処理を記述します。

## viewの記述
最後にviewの記述を行います。

views/likes/_like.html.erb

```ruby
 <%if user_signed_in? %>
    <% if Like.find_by(user_id: current_user.id, post_id: @post.id, comment_id: comment.id) %>
        <%= link_to  post_comment_like_path(@post, comment, comment.likes), {class: "like-link", method: :delete } do %>
          <i class="fas fa-grin-squint-tears unlike-btn"></i>
        <% end %>
        <p class="count"><%= comment.likes.count %></p>
    <% else %>
        <%= link_to post_comment_likes_path(@post, comment), {class: "like-link", method: :post } do %>
          <i class="far fa-grin-squint-tears like-btn"></i>
        <% end %>
        <p class="count"><%= comment.likes.count %></p>
    <% end %>
  <% else %>
      <i class="fas fa-grin-squint-tears unlike-btn"></i><p class="count"><%= comment.likes.count %></p>
  <% end %>
```
user_signed_in?メソッドを使用しログインしていないユーザーには、カウント数だけ表示
されるようにしています。
そして、if文でfind_byメソッドを使用し、idが一致したものがあれば、削除のボタンを表示し、
一致しない時は、いいねボタンが表示されるようにしています。



以上がいいね機能実装の手順になります。
初心者の私にはかなり難しく感じました。
実装できたものの、理解できていない部分もあります。

もし何か間違いや聞きたいことがありましたら、コメント
していただければ幸いです。