8

셀프 조인 (@Shtééf's answer 기준)을 통해 동일한 모델의 레코드간에 다중 관계를 구현하려고합니다. 다음 모델이 있습니다ActiveRecord에서 다중 셀프 조인을 사용하는 다 대다 연관

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:...>] 

내가 뭔가 잘못하고 있는가 작성해야?

답변

8

당신은 has_many 협회에 before_addbefore_remove 콜백을 사용할 수 있습니다 이 솔루션에 대해 this question을 읽을 수 있습니다.

3

여러 개의 다 대다 자체 조인 연결을 만들려면 연결을 관리하기 위해 여러 테이블을 갖는 것이 더 바람직 할 수 있습니다. 그렇게하면 데이터의 관점에서 정확히 무슨 일이 벌어지고 있는지 명확히 알 수 있습니다. 또한 논리적 인 관점에서도 명확합니다. 이 라인을 따라 그래서 뭔가 :

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 

다른 변수는 관리 할 필요가 없습니다. 이해가 되니? 그것은 테이블을 추가하지만 선명도를 향상시킵니다.

+0

그리고 belongs_to가 필요하다는 것은 완전히 확신 할 수는 없지만, t 설정할 시간이 없었습니다. 그는 내 로컬 설치에 전체 운동. 당신은 어떤 결과를 얻었는지에 상관없이 그것을 시도 할 수 있습니다. –

+1

또한이 경로를 따라 가기를 원할 경우이 질문을 확인하십시오. 그것은 당신이하고있는 일들의 선을 따라 더 많이 보여 주지만, 나는 아직도 자신이 조금 부족하다는 선명도를 발견한다. (질문은 질문에 대답되어있다.) http://stackoverflow.com/questions/6426383/rails- 협회 - 사용 - 연관 테이블 - 데이터 테이블 –

2

다음과 같이 나는 모델을 다시 실행합니다 :

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 

참고 : 그것은 보통 때 그들은 하나가 회사를 떠날위한 표시이다 두 관리자에게보고하도록 만들어졌습니다 :-)

+0

귀하의 답변에 감사드립니다. 모든 관계 유형에 대해 추가 테이블을 사용하지 않으려 고했지만, 아마도이 작업을 수행하는 가장 좋은 방법 일 것입니다. – Andrei

2

표시된 관계가 주어진 경우

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 

이 협회 매핑이 조금 간단합니다.이 작품과 employe.managers.create
당신은
또한 콜백에 createbuild instread을 사용할 수 있습니다 사용이 가능해야한다

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 } 

: