在ActiveRecord中具有多个自联接的多对多关联

|| 我正在尝试通过自联接(基于@Shtééf\的答案)在同一模型的记录之间实现多个关系。我有以下型号
create_table :relations, force: true do |t|
  t.references :employee_a
  t.string     :rel_type
  t.references :employee_b
end

class Relation < ActiveRecord::Base
  belongs_to :employee_a, :class_name => \'Employee\'
  belongs_to :employee_b, :class_name => \'Employee\'
end

class Employee < ActiveRecord::Base
  has_many :relations, foreign_key: \'employee_a_id\'
  has_many :reverse_relations, class_name: \'Relation\', foreign_key: \'employee_b_id\'

  has_many :subordinates, through: :relations, source: \'employee_b\', conditions: {\'relations.rel_type\' => \'manager of\'}
  has_many :managers, through: :reverse_relations, source: \'employee_a\', conditions: {\'relations.rel_type\' => \'manager of\'}
end
通过此设置,我可以成功访问每条记录的下属和经理列表。但是,我很难通过以下方式建立关系
e = Employee.create
e.subordinates.create
e.subordinates #=> []
e.managers.create
e.managers #=> []
问题是它没有设置关系类型,所以我不得不写
e = Employee.create
s = Employee.create
e.relations.create employee_b: s, rel_type: \'manager of\'
e.subordinates #=> [#<Employee id:...>]
难道我做错了什么?     
已邀请:
        您可以在has_many关联上使用
before_add
before_remove
回调:
class Employee < ActiveRecord::Base
  has_many :relations, foreign_key: \'employee_a_id\'
  has_many :reverse_relations, class_name: \'Relation\', foreign_key: \'employee_b_id\'

  has_many :subordinates, 
           through: :relations, 
           source: \'employee_b\', 
           conditions: {\'relations.rel_type\' => \'manager of\'}
           :before_add => Proc.new { |employe,subordinate| employe.relations.create(employe_b: subordinate, rel_type: \'manager of\') },
           :before_remove => Proc.new { |employe,subordinate| employe.relations.where(employe_b: subordinate, rel_type: \'manager of\').first.destroy }

  has_many :managers,  
           through: :reverse_relations, 
           source: \'employee_a\', 
           conditions: {\'relations.rel_type\' => \'manager of\'}
           :before_add => Proc.new { |employe,manager| employe.reverse_relations.create(employe_a: manager, rel_type: \'manager of\') },
           :before_remove => Proc.new { |employe,manager| employe.reverse_relations.where(employe_b: subordinate, rel_type: \'manager of\').first.destroy }
这应该可以使您能够使用
employe.managers.create
您可能要在回调中使用
create
build
实例 您也可以阅读有关此解决方案的问题     
        为了创建多个多对多自连接关联,我建议使用多个表来管理连接可能更有意义。这样一来,从数据的角度来看到底发生了什么就很清楚了,从逻辑的角度来看也很清楚。因此,遵循以下原则:
create_table :manage_relation do |t|
  t.references :employee_id
  t.references :manager_id
end
create_table :subordinate_relation do |t|
  t.references :employee_id
  t.references :subordinate_id
end

class Employee < ActiveRecord::Base

  has_many :subordinates, 
           :through => :subordinate_relation, 
           :class_name => \"Employee\", 
           :foreign_key => \"subordinate_id\"
  has_many :managers, 
           :through => :manage_relation, 
           :class_name => \"Employee\", 
           :foreign_key => \"manager_id\"

  belongs_to :employee, 
             :class_name => \"Employee\"
end
这样一来,从编码的角度来看,它不会变得不必要的复杂,并且您可以使用标准集合来访问它,它将为您适当地建立连接,而无需管理它们。因此,这两个集合都应该工作。
employee.managers
employee.subordinates
而且您不必管理任何其他变量。合理?它添加了一个表,但提高了清晰度。     
        我将按照以下方式重做您的模型:
class ManagerRelation < ActiveRecord::Base
  belongs_to :manager, :class_name => \'Employee\'
  belongs_to :subordinate, :class_name => \'Employee\'
end

class Employee < ActiveRecord::Base
  has_many :manager_relations,     :class_name => \"ManagerRelation\",
               :foreign_key => :subordinate_id
  has_many :subordinate_relations, :class_name => \"ManagerRelation\", 
               :foreign_key => :manager_id

  has_many :managers,     :source => :manager,     
               :through => :manager_relations

  has_many :subordinates, :source => :subordinate, 
               :through => :subordinate_relations
end
现在,您可以执行以下操作:
employee.managers
employee.subordinates    
employee.managers << employee2    
employee.subordinates << employee3
注意:通常要求一个人离开公司,向两个经理报告:-)     
        给定呈现的关系
create_table :relations, force: true do |t|
  t.references :employee_a
  t.string     :rel_type
  t.references :employee_b
end


class Employee < ActiveRecord::Base

  has_many :subordinate_relations, :class_name => \"Relation\", :conditions => {:rel_type => \'manager of\'}, :foreign_key => :employee_a
  has_many :subordinates, :through => :subordinate_relations, :source => :subordinate, :foreign_key => :employee_b

  has_many :manager_relations, :class_name => \"Relation\", :conditions => {:rel_type => \'manager of\'}, :foreign_key => :employee_b
  has_many :managers, :through => :manager_relations, :source => :manager, :foreign_key => :employee_a

end


class Relation < ActiveRecord::Base

  belongs_to :manager, :class_name => \"Employee\", :foreign_key => :employee_a
  belongs_to :subordinate, :class_name => \"Employee\", :foreign_key => :employee_b

end


e = Employee.create
e.subordinates.create #Employee ...
e.subordinates #[<Employee ...]

e2 = Employee.create
e2.managers.create #Employee
e2.managers #[<Employee ...]
尽管该解决方案有效-通过将关联与\“ rel_type \”捆绑在一起,我还是有些困惑。在这种情况下-我会说rel_type是冗余的,并且该关系应按以下方式映射:
create_table :relations do |t|
    t.reference :manager
    t.reference :subordinate
end
在这种情况下,关联映射应该更简单一些。     

要回复问题请先登录注册