[Rails]いいね機能実装

  • 6
    Like
  • 0
    Comment

目標

0076efc1fe0ec660007d29f6eb82f0bd.gif

開発環境

・Ruby 2.3.1
・Rails 5.1.3

使用したモデル

userモデル(誰がいいねするか)
closetモデル(どの服にいいねするか)
likeモデル(いいね自身)

likesテーブル作成

userモデルやclosetモデル、closetsコントローラーは作成済みで進めていきます。
まずはターミナルでlikeモデルを作成します。その後migrationファイルを以下のように編集しましょう。

20170827133035_create_likes.rb
class CreateLikes < ActiveRecord::Migration[5.1]
  def change
    create_table :likes do |t|
      t.references :closet, foreign_key: true
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end

アソシエーションを定義する

models/like.rb
class Like < ApplicationRecord
  belongs_to :closet
  belongs_to :user
end
models/closet.rb
class Closet < ApplicationRecord
  has_many :likes, dependent: :destroy

  def like_user(id)
    likes.find_by(user_id: id)
  end
end

closetが削除されたらそれに紐づくlikeも削除したいのでオプションでdependent: :destroyを記述します。

models/user.rb
class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable, :omniauthable

  #いいね機能のアソシエーション
  has_many :likes, dependent: :destroy
end

こちらも先ほど同様にオプションでdependent: :destroyを記述します。

likesコントローラー作成

likes_controller.rb
class LikesController < ApplicationController

  before_action :set_closet, only: [:create, :destroy]

  def create
    @like = current_user.likes.create(closet_id: params[:closet_id])
    @closets = Closet.all
  end

  def destroy
    like = current_user.likes.find_by(closet_id: params[:closet_id])
    like.destroy
    @closets = Closet.all
  end

  private

  def set_closet
    @closet = Closet.find(params[:closet_id])
  end
end

closetsコントローラーのアクション定義

closets.controller.rb
class ClosetsController < ApplicationController

  def index
    @closets = Closet.all
  end
end

ルーティング追加

route.rb
Rails.application.routes.draw do
  resources :closets  do
    resources :likes, only: [:create, :destroy]
  end
end

view作成

今回はslimでviewを記述しています。

closets/index.html.slim
#js-grid-juicy-projects
  /@closetsをrender先でそれぞれを取り出してclosetとして渡す
  = render @closets
closets/_closet.html.slim
/画像用なので記述しなくてよい
.cbp-caption-defaultWrap
  = image_tag closet.thumbnails.first.image, class: "item-thumbnail-capture item-thumbnail-size"
/ここから必要
.like-link
  = render partial: 'likes/like_links', locals:{ closet: closet }

※画像とかじゃなく他の何かにいいねする場合は.like-linkのcssになにかしらの記述いると思います。
いいねボタンを部分テンプレートで渡すのでlikesフォルダをviewsの中に作ります

views/likes/_like_links.html.slim
- if closet.like_user(current_user.id)
  = link_to closet_like_path(closet, closet.like_user(current_user)), method: :delete, class: 'heart-icon', remote: true do
    .fa.fa-heart.fa-2x.heart-red
- else
  = link_to closet_likes_path(closet), method: :post, class: 'empty-heart', remote: true do
    .fa.fa-heart-o.fa-2x.heart-color

ハートマークはgemのfont-awesome-railsを使用して表示させています。
いいねしてるかどうかで条件分岐させ,closet.like_userのlike_userはclosetモデルに記述したメソッドを使ってます。
= link_to closet_like_path(closet, closet.like_user(current_user))の引数なのですが、
第一引数のclosetはclosetsコントローラーからとってこれたのですが、第二引数がコントローラーから持ってくる方法がわからず、モデルに記述したメソッドを使用しました。他にいい方法があれば教えて欲しいです。
ちなみにrake routeした結果がこちらです。
スクリーンショット 2017-09-14 16.47.01.png

jsファイル作成

likes/create.js.erb
$("#js-grid-juicy-projects").html("<%= j(render @closets) %>");
likes/destroy.js.erb
$("#js-grid-juicy-projects").html("<%= j(render @closets) %>");

jsファイルはこれだけです。
以上で完成となります。

参考サイト

http://qiita.com/YuitoSato/items/94913d6a349a530b2ea2