Prevent elevation model selection for each comment

I have three models: User, Comment and Upvote. User-comment is one-to-many, User-to-Upvote is one-to-many, and User-to-Upvote is one-to-many.

I want to do something similar to upvoting done in Stackoverflow. Therefore, when you raise / decrease the arrow, the arrow will stand out and remain highlighted, even if you refresh the page or return to the page days / weeks later.

I am currently doing this:

<% if Upvote.voted?(@user.id, comment.id) %> <%= link_to '^', ... style: 'color: orange;'%> <% else %> <%= link_to '^', ... style: 'color:black;'%> <% end %> 

where is the voted? method voted? as follows:

  def self.voted?(user_id, comment_id) find_by(comment_id: comment_id, user_id: user_id).present? end 

So, if I have 10 comments per page, this will load upvote from my database 10 times, just to check if it exists!

There should be a better way to do this, but I think my brain stops working, so I can't think of it.

+6
source share
4 answers

Assuming you have a relationship

 # user.rb class User has_many :upvotes end 

we can upload comments, the current user and his upvotes:

 # comments_controller.rb def index @comments = Comment.limit(10) @user = current_user user_upvotes_for_comments = current_user.upvotes.where(comment_id: @comments.map(&:id)) @upvoted_comments_ids = user_upvotes_for_comments.pluck(:comment_id) end 

And then change the if condition to look like:

 # index.html.erb <% if @upvoted_comments_ids.include?(comment.id) %> <%= link_to '^', ... style: 'color: orange;'%> <% else %> <%= link_to '^', ... style: 'color:black;'%> <% end %> 

This will require only 2 database queries. Hope this helps.

+7
source

We can do this as follows if you want it to be processed in a single request.

Allows you to verify that the relationship is correct

 # user.rb class User < ActiveRecord::Base has_many :comments has_many :upvotes end # comment.rb class Comment < ActiveRecord::Base belongs_to :user has_many :upvotes end # upvote.rb class Upvote < ActiveRecord::Base belongs_to :user belongs_to :comment end 

Then in the controller

 def index current_user = User.first # current_user may come from devise or any authentication logic you have. @comments = Comment.select('comments.*, upvotes.id as upvote').joins("LEFT OUTER JOIN upvotes ON comments.id = upvotes.comment_id AND upvotes.user_id = #{current_user.id}") end 

And in sight

 # index.html.erb <% @comment.each do |comment| %> <% link_color = comment.upvote ? 'orange' : 'black' %> <%= link_to '^', ...style: "color: #{link_color}" %> <% end %> # And all of your logics ;) 
+4
source

If you are limited to N comments on the page, you can do this in two queries using the limit and offset methods to return a set of 1, 2, ... ith from N for the ith page, something like (the syntax can be turned off, Ruby - not my main language)

 comment_ids = Comments.select("comment_id") .where(user_id: user_id) .order(post_date/comment_id/whatever) .offset(per_page * (page_number - 1)) // assumes 1-based page index .limit(per_page) 

This gives you a list of comment_ids that you can use to request Upvote:

 upvoted_comments = Upvotes.select("comment_id") .where(user_id: user_id, comment_id: comment_ids) 

If you sort comment_ids by a column that also exists in Upvote (for example, if you sort by comment_id ), you can replace the Upvote request with a range request.

Put upvoted_comments in the hash, and you should go - if comment_id is in the hash, then it was saved, otherwise not.

+2
source

I'm not sure if this will avoid redundant requests in this state, but maybe you can enable upvotes when receiving comments:

 @comments = Comment.includes(:upvotes).where(foo: 'bar').limit(10) 

Then in the view:

 <%= link_color = comment.upvotes.map(&:user_id).include?(@user.id) ? 'orange' : 'red' link_to '^', ...style: "color: #{link_color}" %> 
0
source

Source: https://habr.com/ru/post/989213/


All Articles