views:

41

answers:

1

I have a fairly standard blog app with the usual post and comments controller/models along with a user model for leaving comments.

So in the user model I have

has_many :comments

And in the the comments model

belongs_to :post
belongs_to :user

All very straight forward.

In the app I'm creating I want the comments to act a little differently. When the user leaves a comment for the first time then I will create a new comment. However I only want to allow one comment per post per user. If the user tries to leave another comment then we should only update the existing comment.

To do this I have added a this to the user model

has_many :posts, :through => :comment

And in the Posts controller I have the following in the show action.

if post = current_user.posts.find_by_permalink(params[:id])
  @comment = post.comments.find_by_user_id(current_user)
else
  @comment = Comment.new
end

This works in that it checks if the user already made a comment on this post and if so then it will result in an update to the comment rather than a new one posted.

But, the above code doesn't sit right with me. Is there a more elegant solution?

+1  A: 

The way you're doing this is fine, but you should move the logic into the model. I also suggest adding some validation using validates_uniqueness_of.

In Post you could do:

has_many :comments do 
    def find_or_build_for_user(user)
        find_by_user_id(current_user.id) || self.comment.build(:user => user)
    end
end

Then in your controller you'd do:

@comment = post.comments.find_or_build_for_user(current_user)

The above uses association extensions, you can read more about them here:

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

I'm not sure if the above will work as is as I haven't tested it, but it should point you in the right direction.

jonnii
That's perfect. I never knew about association extensions, very useful. Thanks.
KJF
They can be very useful, but try not to overuse them. You can also put them in a separate module and use the :extend option on the association. Remember keep your controller skinny and your models fat! (http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model).
jonnii