Search code examples
ruby-on-railsrspecrails-migrations

Having 2 foreign keys from one table in the same table rails


I am trying to implement comment replying feature to my project, however i am not very sure with the method i m using. My basic idea is to keep all the comments in one table, while having another table comments_replies which will have the parent comment(comment) and the comment(reply). now i have something like this is migrations of comments_replies:

create_table :comments_replies do |t|
  t.integer :parent_comment_id, index: true, foreign_key_column_for: :comments, null: false
  t.integer :reply_comment_id, index: true, foreign_key_column_for: :comments, null: false
  t.timestamps null: false
end

and in model comments_reply.rb

belongs_to :comment, class_name: 'Comment'

and in model comment.rb

has_many :comments_replies, foreign_key: :parent_comment_id, foreign_key: :reply_comment_id

as for the second part since i m trying to use RSPEC for testing purposes, in model comments_reply_spec.rb i have:

require 'rails_helper'

RSpec.describe CommentsReply, type: :model do
  let(:comments_reply) { create(:comments_reply) }
  subject { comments_reply }

  it { is_expected.to respond_to(:parent_comment_id) }
  it { is_expected.to respond_to(:reply_comment_id) }
end

but i am not sure how to test this case properly, so any suggestions would be appreciated


Solution

  • What you are trying to achieve could be done from "Comment" model itself. You just need another column 'parent_id' in "Comment" which will reference to parent Comment in same table. For all those main "Comments"(which are not replied to any comment) column 'parent_id' will be null.

    So your model will look like below

    class Comment 
      belongs_to :parent_comment, foreign_key: :parent_comment_id, class_name: 'Comment'
      has_many :replies, foreign_key: :parent_comment_id, class_name: 'Comment'
    end
    

    With your current approach

    You have specify association with two foreign_key which is wrong. In your "Comment" model you need to association. 1) for all replies of a comment 2) For getting parent comment.

    has_many :comments_replies, foreign_key: :parent_comment_id
    has_many :replies, through: :comment_replies
    
    has_one :parent_comment_reply, foreign_key: :reply_comment_id, class_name: 'CommentReply'
    has_one :parent_comment, through: :parent_comment_reply
    

    Also in your CommentReply model

    belongs_to :parent_comment, foreign_key: :parent_comment_id, class_name: 'Comment'
    belongs_to :reply_comment, foreign_key: :reply_comment_id, class_name: 'Comment'
    

    Your specs looks like below

    require 'rails_helper'
    
    RSpec.describe CommentsReply, type: :model do
      let(:parent_comment){ create(:comment) }
      let(:child_comment) { create(:comment) }
    
      let(:comment_reply) { create(:comment_reply, parent_comment_id: parent_comment.id, reply_comment_id: child_comment.id) }
    
      subject { comment_reply }
    
      it { is_expected.to respond_to(:parent_comment_id) }
      it { is_expected.to respond_to(:reply_comment_id) }
    
      it "should correctly identify the parent/child relationship" do
        expect(comment_reply.reply_comment.id).to be_eql(child_comment.id)
        expect(comment_reply.parent_comment.id).to be_eql(parent_comment.id)
      end
    
    end