views:

360

answers:

2

Let's say that I have two models - one called Post and one other called Video. I then have a third model - Comment - that is polymorphically associated to to each of these models.

I can then easily do post.comments and video.comments to find comments assosciated to records of these models. All easy so far.

But what if I want to go the other way and I want to find ALL posts and videos that have been commented on and display these in a list sorted on the date that the comment was made? Is this possible?

If it helps I'm working on Rails 3 beta.

+1  A: 

Try this:

class Post < ActiveRecord::Base
  has_many :comments, :as => :commentable
  named_scope :with_comments, :joins => :comments, 
                              :order => "comments.created_at DESC"
end

class Video < ActiveRecord::Base
  has_many :comments, :as => :commentable
  named_scope :with_comments, :joins => :comments, 
                              :order => "comments.created_at DESC"
end

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

Now you can run these commands:

Post.with_comments
Video.with_comments

Edit It looks like you want a single list with Videos and Posts. This is quite tricky, but doable. For every page you will have to execute 3 queries.

def commented_videos_posts(page = 1, page_size = 30)
  # query 1: get the lastest comments for posts and videos
  comments = Comment.find_all_by_commentable_type(["Post", "Video"], 
               :select => "commentable_type, commentable_id, 
                              MAX(created_at) AS created_at",
               :group => "commentable_type, commentable_id"
               :order => "created_at DESC", 
               :limit => page_size, :offset => (page-1)*page_size)

  # get the video and post ids
  post_ids, video_ids = [], []
  comments.each do |c|
    post_ids << c.commentable_id if c.commentable_type == "Post"
    video_ids << c.commentable_id if c.commentable_type == "Video"
  end

  posts, videos = {}, {}

  # query 2: get posts
  Post.all(post_ids).each{|p| posts[p.id] = p }
  # query 3: get videos
  Video.all(video_ids).each{|v| videos[v.id] = v }

  # form one list of videos and posts
  comments.collect do |c| 
    c.commentable_type == "Post" ? posts[c.commentable_id] :
                                   videos[c.commentable_id]
  end
end
KandadaBoggu
Thank you for your answer. But what if I want to combine the two? I want a list of both (combined) that is ordered by create_at? A merge if you will…
Erik
So you want ONE list with videos and posts ordered by last comment date? Do you want to get last n objects OR more? Do you need pagination and further filtering?
KandadaBoggu
Exactly! And yes, pagination would be added also but my main problem is just how to combine the two lists… any clues?
Erik
Ah, nice, also a great solution! Thank you for time.
Erik
+1  A: 

probably not the best way, but this worked for me:

#get all posts and videos
posts_and_videos = Comment.all.collect{ |c| c.commentable }

#order by last comment date
posts_and_videos_ordered = posts_and_videos.sort{ |x,y| x.comment_date <=> y.comment_date }

hope this works for you too.

EDIT

i'm also assuming you're using has_many :comments, :as => :commentable and belongs_to :commentable, :polymorphic => true like KandadaBoggu suggested.

EDIT #2

actually, i think i made a mistake and the code above won't work... try this:

(Comment.find(:all, :order => "comment_date")).collect{ |x| x.commentable }
j.
Thanks! I'lll give that I try. And yes, the associations are set up correctly. :)
Erik