【rails】いいね機能実装 (追記あり)
現在railsアプリを製作中です。いいねの実装ができたので残しておきます。
注意!deviseを導入済みで進ます
Likeテーブルを導入
rails g model Like user:references post:references
rails db:migrate
これにより
class CreateLikes < ActiveRecord::Migration[5.2] def change create_table :likes do |t| t.references :user, foreign_key: true t.references :post, foreign_key: true t.timestamps end end end
ができます 次にそれぞれのモデルに書いていきます
Likeモデル
class Like < ApplicationRecord validates :user_id, presence: true, uniqueness: {scope: :post_id} validates :post_id, presence: true belongs_to :post belongs_to :user end
uniquenessヘルパーには一意性チェックの範囲を限定する別の属性を指定する:scopeオプションがあります
参考:
Active Record バリデーション - Railsガイド
Postモデル
class Post < ApplicationRecord belongs_to :user has_many :likes, dependent: :destroy end
dependent: :destroyと書くのはpostが削除されたらlikeも消すようにするため
参考:
has_manyのdependentオプションについて【Ruby on Rails】 - Qiita
最後にUserモデル
class User < ApplicationRecord devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable has_many :posts, dependent: :destroy has_many :likes, dependent: :destroy end
ルーティングの設定
config/routes.rb Rails.application.routes.draw do resources :posts do resources :likes, only: [:create, :destroy] end end
このようにネストした書き方か、progeteにあるような
post 'likes/:post_id/create', to: 'likes#create'
と書かないとparamsでidが取得できなくなります。要注意です。
ネストした書き方だとpost_likes POST /posts/:post_id/likes(.:format) likes#create
と自動的にやってくれますしpathも使えます。
ネストしないと/likes ,to: 'likes#create'
となってしまい、不可能になってしまいます。
viewの作成
rails g controller likes create destroy
コントローラー側
class LikesController < ApplicationController before_action :set_post, only: [:create, :destroy] def create @like = current_user.likes.create(like_params) redirect_to post_path(@post) end def destroy @like = Like.find_by(like_params, user_id: current_user.id) @like.destroy redirect_to post_path(@post) end private def set_post @post = Post.find(params[:post_id]) end def like_params params.permit(:post_id) end end
Postsコントローラの設定
def show @like = Like.new end
今回はPosts#showでいいね出来るようにするのでshowアクションに以下を付け加える
view側
touch app/views/likes/_like.html.slim
-if current_user.likes.find_by(post_id: @post.id) = link_to post_like_path(@post), method: :delete p いいね済 - else = link_to post_likes_path(@post),method: :post p いいね
部分テンプレート
app/views/posts/show.html.slim
..... = render "likes/like"
おわり。
追記
いいねをしたpostのみをユーザーページに表示させるには
#app/controllers/users_controller.rb class UsersController < ApplicationController def show @user = User.find(params[:id]) @liked_post = Post.joins(:likes).where(likes: { user: @user }) end end
@liked_post = Post.includes(:user).joins(:likes).where(likes: { user: @user })
解説
参考:
ActiveRecordのincludes、joins、eager_load等の違いを調べてみた - Qiita
Rails における内部結合、外部結合まとめ - Qiita
似ているようで全然違う!?Activerecordにおけるincludesとjoinsの振る舞いまとめ - Qiita
ActiveRecordのjoinsとpreloadとincludesとeager_loadの違い - Qiita
SELECT "posts".* FROM "posts" INNER JOIN "likes" ON "likes"."post_id" = "posts"."id" WHERE "likes"."user_id" = $1 [["user_id", 1]] 実際に導入してlogを見るとこの様になっています。 絞り込みたいのは,いいねをした@userのlikeデーブルにあるuser_idに紐付いたpostです。。 INNER JOIN 内部結合 ON 結合キー
実際に表示させるには
#app/views/users/show.html.slim - @liked_post.each do |post| p タイトル : #{post.name}
非同期導入
↓続きはこちら 【rails】いいね機能 非同期編 - 技術系ブログ
おしまし。