简体   繁体   中英

Rails 5 adding Likes to Articles and Comments

Rails 5, I am trying to add Likes from scratch without a gem dependency. I am so close to having it down but am being completely stumped by what's happening when Comments get involved.

Storing and saving article.likes worked perfectly. Then I got the comment.likes to work. Now, when I like a Comment, they individually store a new Like (great!), except now Article is not properly saving any likes, and even weirder: article.likes.count gives a TOTAL sum of all it's comments' likes.. I am sure this is an easy fix but I am just totally missing it and I've tried everything. I've gone down some deep rabbit holes for this.

I think the problem lies in routing, or how the models relate.

Articles have many Comments, and both of them have many Likes. Here are the models starting with like.rb:

 class Like < ApplicationRecord
   belongs_to :user
   belongs_to :article
   belongs_to :comment

   # Make sure that one user can only have one like per post or comment
   validates :user_id, uniqueness: { scope: [:article_id, :comment_id] }

 end

article.rb

class Article < ApplicationRecord

  belongs_to :user

  ...

  # destroy associated comments on article deletion
  has_many :comments, dependent: :destroy

  has_many :likes, dependent: :destroy

end

comment.rb

class Comment < ApplicationRecord
  belongs_to :article
  belongs_to :user

  ...

  has_many :likes, dependent: :destroy

end

routes.rb

  ...
  resources :articles, :path => 'blog' do
    resources :likes, only: [:create, :destroy]

    resources :comments do
      resources :likes, only: [:create, :destroy]
    end

  end

the meaty likes_controller.rb. Mind the #{}, EVERYTHING checks out the way it should, so why does the comment.likes.create save correctly, but the article.likes.create does not?? Help, please.

class LikesController < ApplicationController
  before_action :get_signed_in_user
  before_action :comment_or_article

  def create
    if @comment_like
      like_resource(comment)
    else
      like_resource(article)
    end
  end

  def destroy
    if @comment_like
      comment.likes.where(user: current_user).destroy_all
    else
      article.likes.where(user: current_user).destroy_all
    end
    flash[:success] = "Unliked! :("
    redirect_to article_redirect(article)
  end

  private
  def like_resource(obj)
    if obj.likes.where(user: current_user.id).present?
      flash[:error] = "You've already upvoted this! + #{like_params} + #{@comment_like} + #{obj}"
    if @comment_like
      if obj.likes.create(user: current_user, article: @article)
        flash[:success] = "Upvoted Comment! + #{like_params} + #{@comment_like} + #{obj}"
      else
          flash[:error] = "An error prevented you from upvoting."
      end
    elsif obj.likes.create(user: current_user)
      flash[:success] = "Upvoted Blog! + #{like_params} + #{@comment_like} + #{obj}"
    else
      flash[:error] = "An error prevented you from upvoting."
    end
    redirect_to article_path(article)
  end

  def get_signed_in_user
    unless user_signed_in?
      flash[:error] = "Sign in to upvote!"
      redirect_back(fallback_location: articles_path)
    end
  end

  # decide what we are liking
  def comment_or_article
    if comment
      @comment_like = true
    else
      @comment_like = false
    end
  end

  def article
    @article = Article.find(params[:article_id])
  end

  def comment
    unless params[:comment_id].nil?
      @comment = article.comments.find(params[:comment_id])
    end
  end

  def like_params
    params.permit( :article_id, :comment_id).merge(user_id: current_user.id)
  end
end

Finally the like buttons in my articles/show.html.erb:

<%= link_to "Like", article_likes_path(@article), method: :post %>
<%= "#{@article.likes.count}" %>

... # inside loop to show comments:
<%= link_to "Like", article_comment_likes_path(@article, comment), method: :post %>
<%= "#{comment.likes.count}" %>

TLDR: Comment likes work fine, save fine, count individually fine. Article likes do not save, but article.likes.count === article.comments.likes.count. Why? I want article.likes to be completely unique, like it's own comments are.

Thank you in advance.

EDIT: took out belongs_to :comments in like.rb and refactored like_controller.rb main function to

 private
  def like_resource(obj)
    if obj.likes.where(user: current_user.id).present?
      flash[:error] = "You've already upvoted this!"
    elsif obj.likes.create(user: current_user, article: @article)
      flash[:success] = "Upvoted!"
    else
      flash[:error] = "An error prevented you from upvoting."
    end
    redirect_to article_path(article)
  end

Always supplying the @article helps when liking a comment. An Article like would not need a comment_id to save.

Sorry for the long post, hopes this helps someone.

I just figured it out, actually.

class Like < ApplicationRecord
  belongs_to :user
  belongs_to :article
  # belongs_to :comment
end

Commenting the above out, it allows the @article "like" to save without a comment being referenced. Article likes are saved properly. However, article.likes.count still increments whenever an article.comment is liked. This means article.likes is always >= article.comments.likes; which is completely fine.

I just changed the @article.likes to:

  <%= "#{@article.likes.where(comment_id: nil).count}" %>

Filtering out all the strictly article.likes. The comment.likes still work perfectly.

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