いいね機能の実装
初めましてこんにちは!すんぎぃです。
SoundReviewというアプリができるまでの流れをやっています。
今回は、いいね機能の実装方法について話していきます。
1.目的
いいと思った投稿に対していいねということを伝えることができます。また、いいと思った投稿を後でも簡単に見直せるようになります。
2.扱うgem
なし
3.DB設計
4.方法
○いいね機能(同期通信)
①Likeモデルの作成を行います
terminalで以下のコマンドを実行します。
% rails g model Like
② 作成されたマイグレーションファイルを以下のように編集します。
class CreateLikes < ActiveRecord::Migration[6.0]
def change
create_table :likes do |t|
t.integer :item_id, null: false
t.integer :user_id, null: false
t.timestamps
end
end
end
カラムは、外部キーである「user_id」と「item_id」のみになります。
編集した後テーブルを作成します。
terminalにて以下のコマンドを実行します。
% rails db:migration
③アソシエーションの定義を行います。
○likesテーブルのアソシエーションを定義します。
like.rbを以下のように編集してください。
class Like < ApplicationRecord
belongs_to :item
belongs_to :user
end
○userテーブルのアソシエーションを定義します。
user.rbを以下のように編集してください。
class User < ApplicationRecord
##省略
has_many :likes, dependent: :destroy
has_many :like_items, through: :likes, source: :item
##省略
end
中間テーブルであるlike_itemsによってuserがどの投稿をいいねしているのかを簡単に取得することができます。
○itemテーブルのアソシエーションを定義します。
item.rbを以下のように編集してください。
class Item < ApplicationRecord
belongs_to :user
has_many :likes, dependent: :destroy
has_many :like_users, through: :likes, source: :user
end
中間テーブルであるlike_usersによって投稿が誰にいいねされているのか簡単に取得することができます。
④コントローラーの作成を行います。
terminalにて下記コマンドを実行します。
% rails g controller likes
⑤ルーティングの作成を行います。
routes.rbを以下のように編集します。
Rails.application.routes.draw do
mount RailsAdmin::Engine => '/admin', as: 'rails_admin'
devise_for :users
root to: 'items#index'
resources :items do
resources :comments, only: [:new, :create]
resource :likes, only: [:create, :destroy]
collection do
get :search
end
end
resources :items
resources :users, only: [:show,:index,:edit,:update,:destroy]
⑥コントローラーのアクションを作成していきます。
likes_controller.rbを以下のように編集します。
class LikesController < ApplicationController
def create
@item = Item.find(params[:item_id])
like = current_user.likes.new(item_id: @item.id)
like.save
redirect_to item_path(@item.id)
end
def destroy
@item = Item.find(params[:item_id])
@like = Like.find_by(user_id: current_user.id, item_id: @item.id).destroy
redirect_to item_path(@item.id)
end
end
⑦投稿の詳細ページでいいねをできるようにするため、item_controllerのshowアクションに以下の内容を追加していきます。
def show
@message = Message.new
@messages = @item.messages.includes(:user).order('created_at DESC')
@messages.each do |message|
@mess = message
end
##以下追記
@like = Like.new
##ここまで
end
⑧ユーザーが投稿に対してすでにいいねを押しているのか判定するためにuser.rbを以下のように編集します。
##省略
def liked_by?(item_id)
likes.where(item_id: item_id).exists?
end
##省略
⑨ビューを変更します。
○app/views/items/show.html.erbを以下のように編集していきます。
##省略
<div class="item-user">
<div class="profile">
<% if @item.user.image.attached? %>
<%=link_to user_path(@item.user.id),class:"user-icon4" do %>
<%= image_tag @item.user.image, class: "user-icon4" %>
<%end%>
<% else %>
<%=link_to user_path(@item.user.id) ,class:"user-icon4" do %>
<%= image_tag "willy.png", alt: "user-icon", class: "user-icon4" %>
<%end%>
<% end %>
</div>
<div class="item-user-name">
<%= link_to @item.user.nickname, user_path(@item.user.id) %>
</div> <br/>
##以下追記
<%= render partial: 'likes/like', locals: { item: @item} %>
##ここまで
</div>
##省略
##追記
<%if user_signed_in?%>
<%= render partial: 'likes/like2', locals: { item: @item} %>
<%end%>
##追記
##省略
○app/views/likes/_like.html.erbを以下のように編集していきます。
<%if user_signed_in?%>
<div class="good-btn">
<% if current_user.liked_by?(@item) %>
<%= link_to item_likes_path(@item), method: :delete do %>
<i class="fas fa-thumbs-up"></i>
<%end%>
<% else %>
<%= link_to item_likes_path(@item),method: :post do %>
<i class="far fa-thumbs-up"></i>
<%end%>
<% end %>
</div>
<div class="item-count">
<%= @item.likes.count%>
</div>
<%else %>
<div class="heart">
<i class="fas fa-thumbs-up"></i>
</div>
<div class="item-count">
<%= @item.likes.count%>
</div>
<%end%>
○app/views/likes/_like2.html.erbを以下のように編集していきます。
<%if user_signed_in?%>
<div class="good-btn2">
<% if current_user.liked_by?(@item.id) %>
<%= link_to item_likes_path(@item), method: :delete,remote: true do %>
<i class="fas fa-thumbs-up ii"></i>
<%end%>
<% else %>
<%= link_to item_likes_path(@item),method: :post,remote: true do %>
<i class="far fa-thumbs-up ii"></i>
<%end%>
<% end %>
</div>
<%end%>
○FontAwsomeの導入
①gemの導入を行います。
Gemfileに以下の内容を追記します。
# Gemfileに以下を追記
gem 'font-awesome-sass'
terminalで以下のコマンドを実行し上記のgemを導入します。
% bundle install
②application.scssに以下のコードを追記します。
$fa-font-path: '@fortawesome/fontawesome-free/webfonts';
@import '@fortawesome/fontawesome-free/scss/fontawesome';
@import '@fortawesome/fontawesome-free/scss/solid';
@import '@fortawesome/fontawesome-free/scss/regular';
@import '@fortawesome/fontawesome-free/scss/brands';
@import '@fortawesome/fontawesome-free/scss/v4-shims';
○いいね機能(非同期通信)
①app/views/items/show.html.erbを以下のように編集していきます。
<div class="item-user">
<div class="profile">
<% if @item.user.image.attached? %>
<%=link_to user_path(@item.user.id),class:"user-icon4" do %>
<%= image_tag @item.user.image, class: "user-icon4" %>
<%end%>
<% else %>
<%=link_to user_path(@item.user.id) ,class:"user-icon4" do %>
<%= image_tag "willy.png", alt: "user-icon", class: "user-icon4" %>
<%end%>
<% end %>
</div>
<div class="item-user-name">
<%= link_to @item.user.nickname, user_path(@item.user.id) %>
</div> <br/>
<div class="good" id="like-link-<%= @item.id %> ">
<%= render partial: 'likes/like', locals: { item: @item} %>
</div>
<%= link_to "https://twitter.com/share?url=おすすめレビュー #{@item.name} #{request.url}", title: 'Twitter', target: '_blank',class:"twitter" do%>
<i class="fab fa-twitter"></i>
<%end%>
</div>
##省略
##追記
<%if user_signed_in?%>
<div class="good2" id="lik-link-<%= @item.id %> ">
<%= render partial: 'likes/like2', locals: { item: @item} %>
</div>
<%end%>
##追記
##省略
②remote:trueの付与をしていきます。
○app/views/items/show.html.erbを以下のように編集していきます。
<%if user_signed_in?%>
<div class="good-btn">
<% if current_user.liked_by?(@item) %>
<%= link_to item_likes_path(@item), method: :delete,remote: true do %>
<i class="fas fa-thumbs-up"></i>
<%end%>
<% else %>
<%= link_to item_likes_path(@item),method: :post,remote: true do %>
<i class="far fa-thumbs-up"></i>
<%end%>
<% end %>
</div>
<div class="item-count">
<%= @item.likes.count%>
</div>
<%else %>
<div class="heart">
<i class="fas fa-thumbs-up"></i>
</div>
<div class="item-count">
<%= @item.likes.count%>
</div>
<%end%>
○app/views/likes/_like2.html.erbを以下のように編集していきます。
<%if user_signed_in?%>
<div class="good-btn2">
<% if current_user.liked_by?(@item.id) %>
<%= link_to item_likes_path(@item), method: :delete,remote: true do %>
<i class="fas fa-thumbs-up ii"></i>
<%end%>
<% else %>
<%= link_to item_likes_path(@item),method: :post,remote: true do %>
<i class="far fa-thumbs-up ii"></i>
<%end%>
<% end %>
</div>
<%end%>
「remote true」を付与することで、パラメーターがHTML形式ではなくJS形式で送られるようになります。
今回の場合は「remote: true」をlink_toメソッドに付与することで、likes_controller.rbのcreateアクション後は、views/likes/create.js.erbが呼び出されます。
③likes_controller.rbの「redirect_to」の記述を削除します。
class LikesController < ApplicationController
def create
@item = Item.find(params[:item_id])
like = current_user.likes.new(item_id: @item.id)
like.save
end
def destroy
@item = Item.find(params[:item_id])
@like = Like.find_by(user_id: current_user.id, item_id: @item.id).destroy
end
end
④app/views/likesディレクトリ以下にcreate.js.erb , destroy.js.erbファイルを作成します。
⑤部分テンプレートの切り替えができるように、それぞれのjs.erbファイルを編集します。
○app/views/likes/create.js.erbを以下のように編集します。
document.getElementById("like-link-<%= @item.id %> ").innerHTML = '<%= j(render partial: 'likes/like', locals: { item: @item}) %>'
document.getElementById("lik-link-<%= @item.id %> ").innerHTML = '<%= j(render partial: 'likes/like2', locals: { item: @item}) %>'
○app/views/likes/destroy.js.erbを以下のように編集します。
document.getElementById("like-link-<%= @item.id %> ").innerHTML = '<%= j(render partial: 'likes/like', locals: { item: @item}) %>'
document.getElementById("lik-link-<%= @item.id %> ").innerHTML = '<%= j(render partial: 'likes/like2', locals: { item: @item}) %>'
「innerHTMLメソッド」を使うことで特定のHTML要素を置き換えることができます。
「j」とは「escape_javascriptメソッド」の省略形で、部分テンプレート内の改行をエスケープ処理してくれます。
5.まとめ
これにていいね機能の実装は、完成です。
ありがとうございました。
下のように反映されたらいいねは、OK!!です。
この記事が気に入ったらサポートをしてみませんか?