Accessing all comments, on all posts, that a current_user has commented on
This is sorta related to another question I asked recently. But before I implement that question's answer, I wanted to try and do this in a more simple way.
I have built a ruby on rails application that allows users to track their workouts. Workouts also have the ability to be commented on. In my dashboard view for current_user
I am trying to fish out all comments from workouts that current_user has commented on. Here is an example:
User A creates a workout and User B comments on it. User C comments on User A's workout. I want User B to see User C's comment in their dashboard view.
What is the most efficient way to loop through comments and pull out all comments from workouts that a user has commented on? It's like I want to say this but obviously开发者_运维知识库 this won't work:
<% current_user.comments.workouts.comments.each do |comment| %>
I have been experimenting with named_scopes for this but can't seem to figure it out. The associations are as you would expect.
User has_many :workouts
User has_many :comments
Workout belongs_to :user
Workout has_many :comments
Comment belongs_to :user
Comment belongs_to :workout
As you'd probably want to organize the comments according the the workouts that the user had commented on (as opposed to one long undifferentiated string of comments without any context), first I'd suggest using a :has_many => through to aggregate the workouts that the user had commented on, something roughly like (untested, obviously):
has_many :commented_workouts, :through => :comments, :class_name => 'Workout', :source => :workout, :uniq => true, :order => "created_at desc"
Then you could display the comments in your erb something like:
<% current_user.commented_workouts.each do |workout| %>
<%= workout.name %>:
<% workout.comments.each do |comment| %>
<%= comment.text %>
<% end %>
<% end %>
EDIT: you could also do:
<% current_user.commented_workouts.each do |workout| %>
<% workout.comments.sort{|x,y| x.created_at <=> y.created_at }.each do |comment| %>
<%= comment.user.name %> just commented on <%= workout.title %>:
<div><%= comment.text %></div>
<% end %>
<% end %>
EDIT: Or like so (note the limit added to the array):
class User
def watched_comments
commented_workouts.map(&:comments).flatten.sort{|x,y| x.created_at <=> y.created_at }
end
end
# and in the erb:
<% current_user.watched_comments[0,10].each do |comment| %>
<%= comment.user.name %> just commented on <%= comment.workout.title %>:
<div><%= comment.text %></div>
<% end %>
There's a bit of nasty n+1 query fu going on here that might not be real performant tho. Alternately you could try to get everything done in straight sql which would perform better. Something like (sql ninjas no doubt could do better):
EDIT: You can also add a 'limit' option directly in the SQL
has_many :watched_comments, :class_name => 'Comment', :finder_sql => 'select * from comments, workouts where comments.workout_id = workout.id and workouts.id in (select workouts.id from workouts, comments where workouts.id = comments.id and comments.user_id = #{id}) order by comments.created_at desc limit 10'
Something like
<% workouts = []
current_user.comments.each do |comment|
unless workouts.include? comment.workout.id # prevents duplicates
workouts << comment.workout.id
comment.workout.comments.each do |comment|
# now comment refers to all comments for that workout
puts "#{comment.user.name} says #{comment.text}"
end
end
end %>
Essentially, you grab the workout associated with each of their comments and display all the comments for them.
For extra homework ;)
- Flag their own comment with something special
class User < ActiveRecord::Base
has_many :workouts
has_many :comments
def workouts_on_which_i_commented
comments.map{|x|x.workout}.uniq
end
def comment_stream
workouts_on_which_i_commented.map do |w|
w.comments
end.flatten.sort{|x,y| y.created_at <=> x.created_at}
end
end
精彩评论