いいね機能

内容

いいね機能を投稿一覧表示と詳細表示ページにつける

いいね機能

Ajaxを用いて非同期通信でいいね機能を実装する

1. モデルの作成

Likeモデルを作成し、マイグレーションファイルを編集
ターミナル

% rails g model like

likes.rb

t.references :user, null: false, foreign_key: true
t.references :post, null: false, foreign_key: true

ターミナル

% rails db:migrate

2. アソシエーションの記述

3. バリデーションの設定

1つの投稿にユーザーが1回しかいいねできないように、バリデーションを設定する
like.rb

validates :post_id, uniqueness: {scope: :user_id}

4. ルーティングの設定

投稿に紐づくようにルーティングをネストさせる
routes.rb

resources :posts, except: :index do
  resource :likes, only: %i[create destroy]
end

5. コントローラーの設定

コントローラーを作成、createアクションとdestroyアクションを記述
ターミナル

% rails g controller likes

likes_controller.rb

before_action :post_find

def create
  Like.create(user_id: current_user.id, post_id: @post.id)
end

def destroy
  @like = Like.find_by(user_id: current_user.id, post_id: @post.id)
  @like.destroy
end

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

6. ビューの設定

Userモデルにメソッドを追加しておく
user.rb

def liked_by?(post_id)
  likes.where(post_id: post_id).exists?
end

部分テンプレートを作成し、いいねボタンを押せるようにする
ユーザーがいいねボタンをすでに押しているかどうかで条件分岐
_like.html.erb

<% if user_signed_in? %>
  <% if current_user.liked_by?(post.id) %>
    <%= link_to post_likes_path(post.id), method: :delete, remote: true do %>
      <i class="fas fa-heart unlike-btn"></i>
    <% end %>
    <span><%= post.likes.count %></span>
  <% else %>
    <%= link_to post_likes_path(post.id), method: :post, remote: true do %>
      <i class="far fa-heart like-btn"></i>
    <% end %>
    <span><%= post.likes.count %></span>
  <% end %>
<% else %>
  <i class="far fa-heart like-btn"></i><span><%= post.likes.count %></span>
<% end %>

投稿一覧(および詳細)ページでは以下のように記述
このidはjsで操作する際に使用する
index.html.erb

<div id="likes-<%= post.id %>">
  <%= render "shared/like", post: post %>
</div>

7. Ajax(非同期通信)

まずは前提知識からまとめる
- romote: trueとは
一言で言うと、リクエストがhtml形式ではなくjs形式になる
- .js.erbファイルとは
一言で言えば、Rubyを埋め込むことのできるjsファイル
このファイル形式ではコントローラーで定義したインスタンス変数を記述することが可能となる
コントローラーは「app/views/コントローラ名/アクション名.リクエストの形式.〇〇」というファイルを探しに行くらしい
よって、remote: trueでリクエストの種類からレスポンスの種類までが異なることとなる
ファイルは自分でcreate.js.erbとdestroy.js.erbを作成(記述内容は両方とも同じ)
create.js.erb、destroy.js.erb

$('#likes-<%= @post.id %>').html("<%= j(render "shared/like", post: @post ) %>");

上記のjsではid名で操作するビューを取得しているが、このid名が、先ほどビューに入れたid名を取得している
※ちなみにrenderの前に記載の「j」は特殊文字エスケープするためのメソッド

8. rubocop

次回

投稿詳細ページでコメント機能を実装

参考(今回の記事とは無関係)

1. 文字列を途中で区切って、「…」等と表記したい時

truncateを使用して文字列を切り捨てる

# 例)文字数30文字で切り捨てを行う場合
<%= truncate(post.comment, length: 30) %>

2. link_toでアラートを表示させたい時

data-confirmを使用する

# 例)投稿削除ボタンを押した時にアラートを表示したい時
<%= link_to "削除", post_path(post.id), method: :delete, data: {confirm: "投稿を削除してもよろしいですか?"} %>