LoginSignup
4
8

More than 3 years have passed since last update.

[Rails]非同期のいいね機能実装

Last updated at Posted at 2020-03-21

ユーザー(user)が出品した商品(item)にいいねできる機能を実装してます。

Likeモデル、テーブル作成

rails g model Like

like.rb
class Like < ApplicationRecord
end
XXXXXXXXXXX_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.2]
  def change
    create_table :likes do |t|
      t.integer     :user_id
      t.integer     :item_id
      t.timestamps
    end
  end
end

rails db:migrateマイグレーションファイル実行

モデル

通常はuserとitemは1対多の関係ですが、いいね機能実装時に関してはlikeテーブルが加わるので、多対多の関係になります。

user.rb
  has_many :likes, dependent: :destroy
  has_many :like_items, through: :likes, source: :item
item.rb
  has_many :likes, dependent: :destroy
  has_many :liking_users, through: :likes, source: :user

dependent: :destroyはいいねを外した時に、中間likesテーブルにある該当userとitemのレコードを一緒に削除してくれます。

:like_itemsはuserがどのitemをいいねしているのかを取得
:liking_usersはitemがどのuserによっていいねされているのか取得

through: :likesは多対多の関係で中間likeテーブルを経由するための関連付けで記述

source:オプションは関連付け元の名前を指定するため記述

like.rb
class Like < ApplicationRecord
  belongs_to :item, counter_cache: :likes_count
  belongs_to :user
end

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

itemsテーブルにlikes_countカラム追加

rails g migration AddLikes_countToItems

XXXXXXXXXXX_add_likes_count_to_items.rb
class AddLikesCountToItems < ActiveRecord::Migration[5.2]
  def change
    add_column :items, :likes_count, :integer
  end
end

rails db:migrateマイグレーションファイル実行

ルーティング設定

routes.rb
resources :items
  member do
    post   '/like/:item_id' => 'likes#like',   as: 'like'
    delete '/like/:item_id' => 'likes#unlike', as: 'unlike'
  end

いいねをつける時→like 外す時→unlike
as:でルーティングに名前を付けれる。この二つはlike_path,unlike_pathとして使えるようになります。

コントローラー

rails g contoller likes

likes_controller.rb
class LikesController < ApplicationController
  before_action :set_variables

  def like
    like = current_user.likes.new(item_id: @item.id)
    like.save
  end

  def unlike
    like = current_user.likes.find_by(item_id: @item.id)
    like.destroy
  end

  private
  def set_variables
    @item = Item.find(params[:item_id])
    @id_name = "#like-link-#{@item.id}"
  end
end

@id_nameは非同期で使用します。

ビュー

items/show.html.haml
  .option
    = render partial: 'likes/like', locals: { item: @item }

renderを使用し、部分テンプレートへ誘導
likesディレクトリに部分テンプレート_like.html.hamlファイルを作成

likes/_like.html.haml
.option__like{:id => "like-link-#{@item.id}"}
  - if current_user.likes.find_by(item_id: item.id)
    = link_to unlike_item_path(@item.id, @item.id), method: :delete, remote: true, class: "option__like-on" do
      .fas.fa-star
      .option__like-on__text いいね!
      .option__like-on__count
        =item.likes.count
  - else
    = link_to  like_item_path(@item.id, @item.id), method: :post, remote: true, class: "option__like-off" do
      .fas.fa-star
      .option__like-off__text いいね!
      .option__like-off__count
        =item.likes.count

いいねボタンのビューを記述します。

{:id => "like-link-#{@item.id}"}をつけることで@itemのボタンであることを指定します。

remote: trueをつけることでリンクを押した時、ajaxを発火させます。

=item.likes.countでいいねされた数を表示します。

いいねボタンの非同期化

like.js.hamlunlike.js.hamlファイル作成

likes/like.js.haml
$("#{@id_name}").html('#{escape_javascript(render("likes/like", item: @item  ))}');

likes/unlike.js.haml
$("#{@id_name}").html('#{escape_javascript(render("likes/like", item: @item  ))}');

コントローラーで定義した@id_nameを指定し、escape_javascriptで先ほど作成した_likeファイルを埋め込んでます。

Sass

item.show.scss
          .option {
            display: flex;
            justify-content: space-between;
            &__like {
              &-on {
                text-decoration: none;
                padding: 11px 10px;
                border-radius: 40px;
                color: #3CCACE;
                border: 1px solid #ffb340;
                display: flex;
                line-height: 16px;
                .fas.fa-star {
                  padding-right: 5px;
                }
                &__text {
                  padding-right: 5px;
                }
              }
              &-off {
                text-decoration: none;
                padding: 11px 10px;
                border-radius: 40px;
                color: #333;
                border: 1px solid #f5f5f5;
                display: flex;
                line-height: 16px;
                background: #f5f5f5;
                .fas.fa-star {
                  padding-right: 5px;
                }
                &__text {
                  padding-right: 5px;
                }
              }
            }
          }

sassの説明は省略します。

完成イメージ

Image from Gyazo

ユーザーがいいねした商品一覧を表示させたい方はこちらをご覧ください

間違えている部分があったらぜひコメントよろしくお願いします!!

4
8
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
8