見出し画像

いいね機能の実装

初めましてこんにちは!すんぎぃです。
SoundReviewというアプリができるまでの流れをやっています。
今回は、いいね機能の実装方法について話していきます。

1.目的

いいと思った投稿に対していいねということを伝えることができます。また、いいと思った投稿を後でも簡単に見直せるようになります。

2.扱うgem

なし

3.DB設計

スクリーンショット 2021-07-24 10.59.25

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!!です。

画像2


この記事が気に入ったらサポートをしてみませんか?