前提・実現したいこと
「いいね」、「コメント」、「フォロー」のアクションがあった際に関連して、通知機能を実装しています。
Qiita1、2、3を参考に「いいね」と「フォロー」があった際の通知は既に上手く機能しました。
しかし「コメント」をもらった場合のみ、通知テーブルにそのデータが保存されません(もちろんコメントテーブル自体には保存されています)。エラーメッセージは表示されません。
参考にしたサイトと自身のカラム名の違いなどにも当然注意しながら実装しましたが、問題点が分かりませんでした。どなたかお知恵をお貸し頂ければ有難いです。
Schema.rb
Ruby
1ActiveRecord::Schema.define(version: 2020_11_24_045031) do 2 3 create_table "comments", force: :cascade do |t| 4 t.integer "user_id" 5 t.integer "post_id" 6 t.text "body" 7 t.datetime "created_at", null: false 8 t.datetime "updated_at", null: false 9 end 10 11 create_table "notifications", force: :cascade do |t| 12 t.integer "visitor_id", null: false 13 t.integer "visited_id", null: false 14 t.integer "post_id" 15 t.string "action", default: "", null: false 16 t.boolean "checked", default: false, null: false 17 t.datetime "created_at", null: false 18 t.datetime "updated_at", null: false 19 t.integer "comment_id" 20 t.index ["comment_id"], name: "index_notifications_on_comment_id" 21 t.index ["post_id"], name: "index_notifications_on_post_id" 22 t.index ["visited_id"], name: "index_notifications_on_visited_id" 23 t.index ["visitor_id"], name: "index_notifications_on_visitor_id" 24 end 25 26end
Notification.rb
Ruby
1class Notification < ApplicationRecord 2 belongs_to :post, optional: true 3 belongs_to :comment, optional: true 4 5 belongs_to :visitor, class_name: 'User', foreign_key: 'visitor_id', optional: true 6 belongs_to :visited, class_name: 'User', foreign_key: 'visited_id', optional: true 7end
Comment.rb
Ruby
1class Comment < ApplicationRecord 2 validates :body, {presence: true} 3 4 belongs_to :user 5 belongs_to :post 6 7 has_many :notifications, dependent: :destroy 8 9 def create_notification_comment!(current_user, comment_id) 10 temp_ids = Comment.select(:user_id).where(post_id: id).where.not(user_id: current_user.id).distinct 11 temp_ids.each do |temp_id| 12 save_notification_comment!(current_user, comment_id, temp_id['user_id']) 13 end 14 save_notification_comment!(current_user, comment_id, user_id) if temp_ids.blank? 15 end 16 17 def save_notification_comment!(current_user, comment_id, visited_id) 18 notification = current_user.active_notifications.new( 19 post_id: id, 20 comment_id: comment_id, 21 visited_id: visited_id, 22 action: 'comment' 23 ) 24 if notification.visitor_id == notification.visited_id 25 notification.checked = true 26 end 27 notification.save if notification.valid? 28 end 29end
Routes.rb
Ruby
1 get 'notifications/index' => 'notifications#index'
Notification_controller.rb
Ruby
1class NotificationsController < ApplicationController 2 3caches_action :index 4 def index 5 @notifications = @current_user.passive_notifications.includes([:visitor]).order(created_at: :desc).page(params[:page]).per(20) 6 @notifications.where(checked: false).each do |notification| 7 notification.update_attributes(checked: true) 8 end 9 end 10end
Comments_controller.rb
Ruby
1class CommentsController < ApplicationController 2 def create 3 @comment = Comment.new(comment_params) 4 @post = @comment.post 5 Comment.create(body: comment_params[:body], post_id: params[:post_id], user_id: @current_user.id) 6 if @comment.save 7 @post.create_notification_comment!(@current_user, @comment.id) 8 else 9 redirect_back(fallback_location: post_comments_path) 10 end 11 end 12end
Notifications/index.html.erb
Rails
1<div class="notification"> 2 <h1>Notifications</h1> 3</div> 4 5<% if @notifications.exists? %> 6 <div class="users-index"> 7 <%= render @notifications %> 8 </div> 9<% else %> 10 <p>通知はありません</p> 11<% end %>
_notification.html.erb
Rails
1<div> 2 <%= notification_form(notification) %> 3 <span><%= "(#{time_ago_in_words(notification.created_at)} ago)" %></span> 4 <br> 5 <% if !@comment.nil? %> 6 <p class="moderate-font text-center" style="color: #C0C0C0;"><%= @comment %></p> 7 <% end %> 8</div>
helpers/notifications_helper.rb
Ruby
1module NotificationsHelper 2 def notification_form(notification) 3 @visitor = notification.visitor 4 @comment = nil 5 your_item = link_to 'あなたの投稿', post_path(notification), style:"font-weight: bold;" 6 @visitor_comment = notification.comment_id 7 case notification.action 8 when "follow" then 9 tag.a(notification.visitor.name, href:user_path(@visitor), style:"font-weight: bold;")+"があなたをフォローしました" 10 when "like" then 11 tag.a(notification.visitor.name, href:user_path(@visitor), style:"font-weight: bold;")+"が"+tag.a('あなたの投稿', href:post_path(notification.post_id), style:"font-weight: bold;")+"にいいねしました" 12 when "comment" then 13 @comment = Comment.find_by(id: @visitor_comment)&.body 14 tag.a(@visitor.name, href:post_path(@visitor), style:"font-weight: bold;")+"が"+tag.a('あなたの投稿', href:post_path(notification.post_id), style:"font-weight: bold;")+"にコメントしました" 15 end 16 end 17end
補足情報(FW/ツールのバージョンなど)
ruby 2.6.4p104
RubyGems 3.0.3
Rails 5.2.3
追記 post.rb
class Post < ApplicationRecord validates :title, {presence: true, length: {maximum: 50}} # validates :level, {presence: true} # validates :maximum, {presence: true} # validates :message, {presence: true, length: {maximum: 140}} # validates :user_id, {presence: true} belongs_to :user has_many :likes, dependent: :destroy has_many :comments, dependent: :destroy def user return User.find_by(id: self.user_id) end has_many :notifications, dependent: :destroy def create_notification_like!(current_user) temp = Notification.where(["visitor_id = ? and visited_id = ? and post_id = ? and action = ? ", current_user.id, user_id, id, 'like']) if temp.blank? notification = current_user.active_notifications.new( post_id: id, visited_id: user_id, action: 'like' ) if notification.visitor_id == notification.visited_id notification.checked = true end notification.save if notification.valid? end end def create_notification_comment!(current_user, comment_id) temp_ids = Comment.select(:user_id).where(post_id: id).where.not(user_id: current_user.id).distinct temp_ids.each do |temp_id| save_notification_comment!(current_user, comment_id, temp_id['user_id']) end save_notification_comment!(current_user, comment_id, user_id) if temp_ids.blank? end def save_notification_comment!(current_user, comment_id, visited_id) notification = current_user.active_notifications.new( post_id: id, comment_id: comment_id, visited_id: visited_id, action: 'comment' ) return if notification.visited_id == notification.visitor_id if notification.visitor_id == notification.visited_id notification.checked = true end notification.save if notification.valid? end end
Commentテーブルのデータベースの中身
[27] pry(main)> Comment.all Comment Load (0.4ms) SELECT "comments".* FROM "comments" +----+---------+---------+------+-------------------------+-------------------------+ | id | user_id | post_id | body | created_at | updated_at | +----+---------+---------+------+-------------------------+-------------------------+ | 1 | 23 | 2 | hoge | 2020-11-29 09:09:27 UTC | 2020-11-29 09:09:27 UTC | | 2 | 23 | 2 | huga | 2020-11-29 09:10:10 UTC | 2020-11-29 09:10:10 UTC | +----+---------+---------+------+-------------------------+-------------------------+ 2 rows in set [28] pry(main)> Comment.last(2).pluck(:body) Comment Load (0.6ms) SELECT "comments".* FROM "comments" ORDER BY "comments"."id" DESC LIMIT ? [["LIMIT", 2]] => ["hoge", "huga"] [29] pry(main)>
###【追記2】ストロングパラメータ変更後
comments.controller.rb
内のストロングパラメータを変更しました。
Ruby
1 @comment = Comment.new(comment_params) 2 @post = @comment.post 3 4 Comment.create(body: comment_params[:body], post_id: params[:post_id], user_id: @current_user.id) 5 if @comment.save! 6 @post.create_notification_comment!(@current_user, @comment.id) 7 redirect_back(fallback_location: post_comments_path) #save出来ても出来なくても、以下のルートにリダイレクト。 8 else 9 redirect_back(fallback_location: post_comments_path) 10 end 11 end 12 13def comment_params 14 params.require(:comment).permit(:body) 15end 16 17↓ # comment_paramsの中身のみ変更。 18 19def comment_params 20 params.require(:comment).permit(:body).merge(post_id: params[:post_id], user_id: @current_user.id) 21end
するとコメントを送信する度に2重に保存されるようになった(つまりcomment_params
が機能する様になった)ので、以下の1行を消す事でコメントが通常通り保存されるようになりました。
Comment.create(body: comment_params[:body], post_id: params[:post_id], user_id: @current_user.id)
それでも通知テーブルにコメントのデータは保存されず、コマンドにて以下のエラーが出ました。
NoMethodError (undefined method `create_notification_comment!' for #<Post:0x000000000f0c16b0> Did you mean? create_notification_like!):
どうやらcreate_notification_comment!
はpost.rb
内に記述しないといけない(?)ようだったので、定義をcomment.rb
からpost.rb
に移行させました。これにより、post.rb
内でのcomment
に関する記述は以下の一行のみになりました。
has_many :notifications, dependent: :destroy
するとようやく、コメントを送信後、通知テーブルにそのデータが保存されるようになりました。またビューのnotifications/index
でもしっかり通知が呼び出されました。
しかしあと2点だけ問題が出てきました。
- コメントを削除しても通知のデータが残ったままになる(
dependent: :destroy
)が機能しない。 - ログイン中のユーザーのコメントも、本人の通知に表示されてしまう。
2に関して、action
はtrue
で保存されているので、保存のされ方は問題ないかと思います。後はビュー内でも、本人には通知がいかないようにする旨の記述を加える必要があると言う事でしょうか?
回答2件
あなたの回答
tips
プレビュー