简体   繁体   中英

How to send notifications to all commentors on polymorphic comments in rails?

I am using polymorphic association for comments. I want to send notification to all user who comments on post.

  • Suppose user1 posted a post.
  • User2 comments on it.
  • User1 gets notification (users = User.joins(:posts).where(:posts => {id: params[:post_id]})) that " User2 posted a comment "
  • Again, User1 comments.
  • Now, User2 should get a notification (users= ????)
  • If user2, user3, user4 comments then all the related users should get notifications.

comment.rb

class Comment < ApplicationRecord
    belongs_to  :user
    belongs_to :commentable, polymorphic: true
    has_many :comments, as: :commentable
    validates :comment, presence: true

    after_create :notifications
    def notifications
       #Create Notification             
       users =  ????
       (users-[@current_user]).each do |user|
        Resque.enqueue(NotifyJob, Notification.create(
        recipient: user,
        actor: @current_user,
        action: "posted",
        notifiable: @comment))
        end
    end

end

user.rb

 class User < ApplicationRecord
   has_many :posts, dependent: :destroy
   has_many :comments, as: :commentable, dependent: :destroy
   has_many :notifications, foreign_key: :recipient_id, dependent: :destroy
 end

post.rb

 class Post < ApplicationRecord
    belongs_to :user
    validates :comment, presence: true
    validates :user_id, presence: true
    has_many :comments, as: :commentable
 end

comments_controller.rb

module Api 
module V1
    class CommentsController < ApplicationController
        skip_before_action :verify_authenticity_token
        before_action :authorize_request
        before_action :find_commentable

        def new
            @comment = Comment.new
        end

        def create
            @comment = @commentable.comments.new(comment_params)
            @comment.user = @current_user

            if @comment.save
                render json: @comment, status: :created

            else
                render json: { errors: @comment.errors.full_messages },
                status: :unprocessable_entity
            end
        end

        def show
            @comment = Comment.find(params[:id])
            if !@comment.nil?
                render json: @comment, status: :ok
            else
                render json: {errors: @comment.errors.full_messages}, status: :unprocessable_entity
            end
        end

        private

        def comment_params
            params.permit(:comment)
        end

        def find_commentable
            @commentable = Comment.find_by_id(params[:comment_id]) if params[:comment_id]
            @commentable = Post.find_by_id(params[:post_id]) if params[:post_id]
        end
    end
end
end

Your first problem is in User model. You have

has_many :comments, as: :commentable, dependent: :destroy

but Comment model belongs_to :user and have user_id , it means that in User should be

has_many :comments, dependent: :destroy

In this case you can get all user comments easily with User.first.comments

The second one is callback. Vyacheslav Loginov is right about bad practice to put this complex logic inside the controller action, but callbacks are not good practice too. Not sure which of them is worse. You will create notification on every comment creation, even from console. Notifications will be created in every test setup where you create commnets. Not sure if that it is what you really want.

Better option is to create separate ServiceObject to handle notifications

class CommentsController < ApplicationController
  def create
    @comment = @commentable.comments.new(comment_params)
    @comment.user = @current_user

    if @comment.save
      NotificationSender.new(@comment).comment_notification
      render json: @comment, status: :created
    else
      render json: { errors: @comment.errors.full_messages },
      status: :unprocessable_entity
    end
  end
end

class NotificationSender
  def initialize(resource)
    @resource = resource
  end

  # you can add here all other notifications as methods
  def comment_notification
    users = User.where(id: @resource.commentable.comments.pluck(:user_id)).
                 where.not(id: @resource.user_id)
    users.each do |user|
      Resque.enqueue(
        NotifyJob, 
        Notification.create(
          recipient: user,
          actor: @resource.user,
          action: "posted",
          notifiable: @resource
        )
      )
    end
  end
end

I didn't make much changes. I just made changes like this on:

comment.rb

class Comment < ApplicationRecord
   belongs_to  :user
   belongs_to :commentable, polymorphic: true
   has_many :comments, as: :commentable
   validates :comment, presence: true

   private
   after_commit :create_notifications, on: [:create]
   #Create Notification
   def create_notifications 
     commentable = self.commentable 
     commentors= Comment.where(commentable: commentable).pluck(:user_id)
     users = User.find(commentors)-[self.user]
       users.each do |user|    
         Resque.enqueue(NotifyJob, Notification.create(
         actor: self.user,
         recipient: user,
         action: "posted",
         notifiable: self.commentable))
       end
   end
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM