Search code examples
ruby-on-railspolymorphic-associations

How would a 'commentable' polymorphic association work on a 'user' model itself?


I'm learning rails and trying out polymorphic association. I have listed below a couple of simple models for illustration. Model associations seems to works fine as expected. But what if a user (commenter) would like to leave a comment for a another user? I can't seems to get it to work with these configuration. How do I go about doing so?

class User < ApplicationRecord
  #  username, email, password_digest
  has_many :comments, as: :commentable, dependent: :destroy
end

class Project < ApplicationRecord
  # title, completed, user_id
  has_many :comments, as: :commentable, dependent: :destroy
end

class Comment < ApplicationRecord
  # commenter_id, commentable_type, commentable_id, body
  belongs_to :commentable, polymorphic: true
end

in console... setup

user1 = User.frist
user2 = User.last
project = Project.first

pro_comment = project.comments.new(body: 'some text')
pro_comment.commenter_id = user1.id
pro_comment.save

user_comment = user2.comments.new(body: 'some text')
user_comment.commenter_id = user1.id
user_comment.save

expected and actual results

Comment.all => successfully lists pro_comment & user_comment

But...
Comment.find_by(commenter_id: 1) => only listed the pro_comment 
(what am I doing wrong?)

Also.. user1.comments => returned an empty object... was expecting 2 objects, as you can see below it's not referencing 'commenter_id' .... result...

comment Load (0.5ms)  SELECT  "comments".* FROM "comments" WHERE 
"comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2 
LIMIT $3  [["commentable_id", 1], ["commentable_type", "User"], 
["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy []>

I also tried ... user1.comments.where(commenter_id: 1) >> which returned...

comment Load (0.4ms)  SELECT  "comments".* FROM "comments" WHERE
 "comments"."commentable_id" = $1 AND "comments"."commentable_type" = $2 
AND "comments"."commenter_id" = $3 LIMIT $4  [["commentable_id", 1],
["commentable_type", "User"], ["commenter_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::AssociationRelation []>

Not sure what I'm doing wrong. Could someone please point me in the right direction. I thank you for your time.


Solution

  • find_by returns only one record, try Comment.where(commenter_id: 1) instead.

    For user1.comments being empty, you are mixing the relationships. You should have 2 relationships: comment belongs to a commentable object (a project or a user) and comments also belongs to a commenter (the user you set as commenter_id).

    It makes sense for user1.comments to be empty since the user is the commenter on both comments, it's not the commentable. user2.comments shouldn't be empty, same for project.comments

    Try something like this:

    class User < ApplicationRecord
      has_many :comments_done, class_name: 'Comment', inverse_of: :commenter
      has_many :comments, as: :commentable, dependent: :destroy
    end
    
    class Comment < ApplicationRecord
      belongs_to :commenter, class_name: 'User'
      belongs_to :commentable, polymorphic: true
    end
    

    (check the guide, I may be missing some config option https://guides.rubyonrails.org/v5.2/association_basics.html#has-many-association-reference)

    Now you can use user1.comments_done and user1.comments for comments done by the user and done at the user.