class PostImplication < ApplicationRecord self.primary_key = :post_id, :parent_post_id belongs_to :post, inverse_of: :parent_post_implications belongs_to :parent_post, class_name: 'Post', inverse_of: :child_post_implications validates :post_id, presence: true, uniqueness: { scope: :parent_post_id } validates :parent_post_id, presence: true validate :parent_post_mustnt_be_itself validate :parent_post_mustnt_create_cycle private def parent_post_mustnt_be_itself if parent_post_id == post_id errors.add :parent_post_id, '親投稿に同じ投稿を設定することはできません.' end end def parent_post_mustnt_create_cycle return if post_id.blank? || parent_post_id.blank? return if errors[:parent_post_id].present? ancestor_ids = Set.new frontier_ids = [parent_post_id] until frontier_ids.empty? if frontier_ids.include?(post_id) errors.add :parent_post_id, :invalid return end ancestor_ids.merge(frontier_ids) frontier_ids = PostImplication.where(post_id: frontier_ids) .distinct.pluck(:parent_post_id) .reject { ancestor_ids.include?(_1) } end end end