ruby-on-railsrubymodel-view-controllerpublic-activitysocial-media-like

Limit user to 1 like?


How can we limit a user to 1 like per comment?

comments_controller.rb

  def like
    @comment = Comment.find(params[:id])
    @comment.increment!(:likes)
    @comment.create_activity :like
    flash[:success] = 'Thanks for liking!'
    redirect_to(:back)
  end

_comments.html.erb

 <% @comments.each do |comment| %>
    <%= User.find(comment.user_id).name %>
    <%= simple_format comment.content %>
    <%= pluralize(comment.likes, 'like') %>
    <%= link_to content_tag(:span, '', class: 'glyphicon glyphicon-thumbs-up') + 
    ' Like it', like_comment_path(:id => comment.id), method: :post %>
 <% end %>

valuation.rb

class Valuation < ActiveRecord::Base
  belongs_to :user
  has_many :comments, as: :commentable
  include PublicActivity::Model
  tracked owner: ->(controller, model) { controller && controller.current_user }
end

routes.rb

resources :valuations do
  resources :comments
  member do
    post :like
  end
end

I followed along this tutorial: http://www.sitepoint.com/activity-feeds-rails/ to implement the public_activity gem and the like button.

Thank you for your time and expertise!

UPDATE

class CommentsController < ApplicationController
    before_action :load_commentable
  before_action :set_comment, only: [:show, :edit, :update, :destroy, :like]
  before_action :logged_in_user, only: [:create, :destroy]

    def index
        @comments = @commentable.comments
    end

    def new
        @comment = @commentable.comments.new
    end

    def create
        @comment = @commentable.comments.new(comment_params)
        if @comment.save
            @comment.create_activity :create, owner: current_user 
            redirect_to @commentable, notice: "comment created."
        else
            render :new
        end
    end

    def edit
        @comment = current_user.comments.find(params[:id])
    end

    def update
        @comment = current_user.comments.find(params[:id])
        if @comment.update_attributes(comment_params)
            redirect_to @commentable, notice: "Comment was updated."
        else
            render :edit
        end
    end

    def destroy
        @comment = current_user.comments.find(params[:id])
        @comment.destroy
        @comment.create_activity :destroy, owner: current_user
        redirect_to @commentable, notice: "comment destroyed."
    end

  def like
    @comment = Comment.find(params[:id])
    @comment.increment!(:likes)
    @comment.create_activity :like
    flash[:success] = 'Thanks for liking!'
    redirect_to(:back)
  end

private
  def set_comment
    @comment = Comment.find(params[:id])
  end

    def load_commentable
        resource, id = request.path.split('/')[1, 2]
        @commentable = resource.singularize.classify.constantize.find(id)
    end

    def comment_params
        params[:comment][:user_id] = current_user.id
        params.require(:comment).permit(:content, :commentable, :user_id)
    end
end

comment.rb

class Comment < ActiveRecord::Base
    include PublicActivity::Common
    # tracked except: :update,  owner: ->(controller, model) { controller && controller.current_user }
    belongs_to :commentable, polymorphic: true
    belongs_to :user
end

Solution

  • I think you could create additional model:

    class CommentLike
       belongs_to :comment
       belongs_to :user
       validates :user, uniqueness: { scope: :comment}
    end
    
    class User
      has_many :comment_likes
    end
    
    class Comment
      has_many :comment_likes
    end
    
    def like
      @comment = Comment.find(params[:id])
      if current_user.comment_likes.create(comment: @comment)
        @comment.increment!(:likes)
        @comment.create_activity :like       
        flash[:success] = 'Thanks for liking!'
      else
        flash[:error] = 'Two many likes'
      end
      redirect_to(:back)
    end
    

    It'll solves you problem. If storing likes_count in comment is neccessary, you could use Rails' counter_cache.