यह सवाल इतना आसान दिखाई जटिल जवाब हो सकता है कि कैसे हास्यास्पद है। इस मामले में, रिफ्लेक्सिव पेरेंट/चाइल्ड रिलेशनशिप को लागू करना काफी सरल है, लेकिन पिता/माता और भाई बहन संबंध जोड़ना कुछ मोड़ बनाता है।
शुरू करने के लिए, हम अभिभावक-बाल संबंधों को पकड़ने के लिए टेबल बनाते हैं। रिश्ता दो विदेशी कुंजी, संपर्क में दोनों की ओर इशारा करते हैं:
create_table :contacts do |t|
t.string :name
end
create_table :relationships do |t|
t.integer :contact_id
t.integer :relation_id
t.string :relation_type
end
रिश्ता मॉडल हम माता-पिता का कहना है में वापस संपर्क करने के लिए:
class Relationship < ActiveRecord::Base
belongs_to :contact
belongs_to :father, :foreign_key => :relation_id, :class_name => "Contact",
:conditions => { :relationships => { :relation_type => 'father'}}
belongs_to :mother, :foreign_key => :relation_id, :class_name => "Contact",
:conditions => { :relationships => { :relation_type => 'mother'}}
end
और संपर्क में उलटा संघों को परिभाषित:
class Contact < ActiveRecord::Base
has_many :relationships, :dependent => :destroy
has_one :father, :through => :relationships
has_one :mother, :through => :relationships
end
अब एक रिश्ता बनाया जा सकता है:
@bart = Contact.create(:name=>"Bart")
@homer = Contact.create(:name=>"Homer")
@bart.relationships.build(:relation_type=>"father",:father=>@homer)
@bart.save!
@bart.father.should == @homer
,
@bart.build_father(@homer)
@bart.save!
एक संपर्क के बच्चों ढूंढने के लिए:
यह इतना महान नहीं है, क्या हम वास्तव में चाहते हैं एक कॉल में संबंध बनाने के लिए है:
class Contact < ActiveRecord::Base
def build_father(father)
relationships.build(:father=>father,:relation_type=>'father')
end
end
तो हम क्या कर सकते हैं संपर्क करने के लिए एक दायरा जोड़ें (सुविधा के लिए) एक उदाहरण विधि:
scope :children, lambda { |contact| joins(:relationships).\
where(:relationships => { :relation_type => ['father','mother']}) }
def children
self.class.children(self)
end
Contact.children(@homer) # => [Contact name: "Bart")]
@homer.children # => [Contact name: "Bart")]
भाई बहन मुश्किल भाग हैं। हम Contact.children विधि का लाभ उठा सकें और परिणामों में हेरफेर:
def siblings
((self.father ? self.father.children : []) +
(self.mother ? self.mother.children : [])
).uniq - [self]
end
इस गैर इष्टतम है, क्योंकि father.children और mother.children ओवरलैप होगा (इस प्रकार uniq
के लिए की जरूरत है), और अधिक कुशलता से किया जा सकता है आवश्यक एसक्यूएल (एक अभ्यास के रूप में छोड़ दिया :)) को ध्यान में रखते हुए, लेकिन ध्यान में रखते हुए कि self.father.children
और self.mother.children
आधे भाई बहनों (एक ही पिता, अलग मां) के मामले में ओवरलैप नहीं होगा, और एक संपर्क में पिता नहीं हो सकते या एक मां
# app/models/contact.rb
class Contact < ActiveRecord::Base
has_many :relationships, :dependent => :destroy
has_one :father, :through => :relationships
has_one :mother, :through => :relationships
scope :children, lambda { |contact| joins(:relationships).\
where(:relationships => { :relation_type => ['father','mother']}) }
def build_father(father)
# TODO figure out how to get ActiveRecord to create this method for us
# TODO failing that, figure out how to build father without passing in relation_type
relationships.build(:father=>father,:relation_type=>'father')
end
def build_mother(mother)
relationships.build(:mother=>mother,:relation_type=>'mother')
end
def children
self.class.children(self)
end
def siblings
((self.father ? self.father.children : []) +
(self.mother ? self.mother.children : [])
).uniq - [self]
end
end
# app/models/relationship.rb
class Relationship < ActiveRecord::Base
belongs_to :contact
belongs_to :father, :foreign_key => :relation_id, :class_name => "Contact",
:conditions => { :relationships => { :relation_type => 'father'}}
belongs_to :mother, :foreign_key => :relation_id, :class_name => "Contact",
:conditions => { :relationships => { :relation_type => 'mother'}}
end
# spec/models/contact.rb
require 'spec_helper'
describe Contact do
before(:each) do
@bart = Contact.create(:name=>"Bart")
@homer = Contact.create(:name=>"Homer")
@marge = Contact.create(:name=>"Marge")
@lisa = Contact.create(:name=>"Lisa")
end
it "has a father" do
@bart.relationships.build(:relation_type=>"father",:father=>@homer)
@bart.save!
@bart.father.should == @homer
@bart.mother.should be_nil
end
it "can build_father" do
@bart.build_father(@homer)
@bart.save!
@bart.father.should == @homer
end
it "has a mother" do
@bart.relationships.build(:relation_type=>"mother",:father=>@marge)
@bart.save!
@bart.mother.should == @marge
@bart.father.should be_nil
end
it "can build_mother" do
@bart.build_mother(@marge)
@bart.save!
@bart.mother.should == @marge
end
it "has children" do
@bart.build_father(@homer)
@bart.build_mother(@marge)
@bart.save!
Contact.children(@homer).should include(@bart)
Contact.children(@marge).should include(@bart)
@homer.children.should include(@bart)
@marge.children.should include(@bart)
end
it "has siblings" do
@bart.build_father(@homer)
@bart.build_mother(@marge)
@bart.save!
@lisa.build_father(@homer)
@lisa.build_mother(@marge)
@lisa.save!
@bart.siblings.should == [@lisa]
@lisa.siblings.should == [@bart]
@bart.siblings.should_not include(@bart)
@lisa.siblings.should_not include(@lisa)
end
it "doesn't choke on nil father/mother" do
@bart.siblings.should be_empty
end
end
आप महोदय एक रेल और स्टैक ओवरफ्लो राक्षस हैं (आपके उत्तरों में चश्मा !?) कमाल !! अगर मैं तुम्हें चुंबन दूंगा! धन्यवाद :) –
आह .. हालांकि एक विचार, क्या यह संपर्क मॉडल में पिता_आईडी और mother_id जोड़ने के लिए काम नहीं करेगा और फिर has_many जोड़ें: बच्चों,: class_name => "संपर्क", finder_sql => 'चयन * संपर्कों से जहां संपर्क .father_id = # {id} या contact.mother_id = # {id} "और has_many: भाई बहनें: class_name =>" संपर्क करें ", finder_sql => 'चयन * संपर्कों से जहां contact.father_id = # {father_id} या संपर्क .mother_id = # {mother_id} '? बस एक विचार: पी –
आप इसे एक तालिका में कर सकते हैं, लेकिन इससे आपको उन रिश्तों तक सीमित कर दिया जाएगा जिन्हें विदेशी कुंजी के माध्यम से निर्दिष्ट किया जा सकता है। एक अलग तालिका के साथ आपके पास निर्दिष्ट करने के लिए लचीलापन है अन्य रिश्ते के प्रकार, जैसे 'गॉडफादर' या 'चाचा'। – zetetic