8

मेरे एप्लिकेशन में एक नौकरी मॉडल है। सिस्टम में प्रत्येक नौकरी में contact है। यह उस व्यक्ति की तरह है जिसे आप कॉल करेंगे यदि आपको नौकरी के बारे में कोई सवाल पूछने की ज़रूरत है। एक संपर्क या तो client या ग्राहक के कर्मचारी (ClientEmployee) हो सकता है।ActiveRecord को जोड़ना :: पॉलिमॉर्फिक एसोसिएशन के माध्यम से पूछे जाने वाले संबंध

class Job < ActiveRecord::Base 
    belongs_to :contact, polymorphic: true 
end 

class Client < ActiveRecord::Base 
    has_many :jobs, as: :contact 
    has_many :employees, class_name: 'ClientEmployee' 
end 

class ClientEmployee < ActiveRecord::Base 
    belongs_to :client 
    has_many :jobs, as: :contact 
end 

ग्राहकों के पास commissioned_jobs का विचार है। ग्राहकों ने नौकरियां शुरू की हैं वे नौकरियां हैं जिनके लिए ग्राहक संपर्क है या ग्राहक के कर्मचारियों में से एक संपर्क है।

class Client < ActiveRecord::Base 
    has_many :jobs, as: :contact 
    has_many :employee_jobs, through: :employees, source: :jobs 

    def commissioned_jobs 
    jobs << employee_jobs 
    end 
end 

एक तरफ: यह विधि एक हैक का एक सा है क्योंकि यह एक ActiveRecord::Relation से एक सरणी देता है बल्कि है। यह भी दिलचस्प है कि अगर मैं कर्मचारी_jobs में नौकरियों को जोड़ना चाहता हूं तो यह उड़ाता है। यह मेरे उद्देश्यों के लिए कर सकता है या नहीं कर सकता है।

मैं Client पर with_commissioned_jobs नामक एक दायरा जोड़ना चाहता हूं। यह उन सभी ग्राहकों को वापस लौटाएगा जिनके पास नौकरियां हैं या जिनके पास नौकरियां हैं।

class Client < ActiveRecord::Base 
    def self.with_commissioned_jobs 
    # I can get clients with jobs using: joins(:jobs). How do 
    # I also include clients with employees who have jobs? 
    end 
end 

मैं इस विधि को कैसे कार्यान्वित करूं?

मैं रेल 3.2.9 का उपयोग कर रहा हूं।

अद्यतन:

मैं कुछ प्रगति की है और मैं अब दो तरीकों, जिनमें से प्रत्येक मैं क्या जरूरत का आधा करता है।

class Client < ActiveRecord::Base 
    # Return all clients who have an employee with at least one job. 
    def self.with_employee_jobs 
    joins(employees: :jobs) 
    # SQL: SELECT "clients".* FROM "clients" INNER JOIN "client_employees" ON "client_employees"."employer_id" = "clients"."id" INNER JOIN "jobs" ON "jobs"."contact_id" = "client_employees"."id" AND "jobs"."contact_type" = 'ClientEmployee' 
    end 

    # Return all clients who have at least one job. 
    def self.with_jobs 
    joins(:jobs) 
    # SQL: SELECT "clients".* FROM "clients" INNER JOIN "jobs" ON "jobs"."contact_id" = "clients"."id" AND "jobs"."contact_type" = 'Client' 
    end 
end 

अब सभी मैं क्या करने की जरूरत गठबंधन इन दोनों विधि एक ActiveRecord::Relation में कहता है। मैं स्पष्ट रूप से यह कर सकते हैं:

def self.with_commissioned_jobs 
    with_jobs + with_employee_jobs 
    end 

समस्या यह है कि कि Relation का एक उदाहरण के बजाय एक सरणी देता है और मैं इस पर श्रृंखला अधिक दायरे नहीं कर सकता।

अद्यतन 2:

merge का उपयोग या तो काम करने के लिए प्रकट नहीं होता। यहां एआर क्वेरी और परिणामी एसक्यूएल है।

joins(:jobs).merge(joins(employees: :jobs)) 

SELECT "clients".* FROM "clients" INNER JOIN "jobs" 
    ON "jobs"."contact_id" = "clients"."id" 
    AND "jobs"."contact_type" = 'Client' 
    INNER JOIN "client_employees" 
    ON "client_employees"."employer_id" = "clients"."id" 
    INNER JOIN "jobs" "jobs_client_employees" 
    ON "jobs_client_employees"."contact_id" = "client_employees"."id" 
    AND "jobs_client_employees"."contact_type" = 'ClientEmployee' 

वैसे, यहां परीक्षण हैं जिन्हें मैं पास करने की कोशिश कर रहा हूं। पहला परीक्षण विफल रहता है क्योंकि जब मैं विलय का उपयोग करता हूं तो शून्य परिणाम होते हैं।

describe "with_commissioned_jobs" do 
    # A client with a job. 
    let!(:client_with) { create :client } 
    let!(:job) { create :job, contact: client_with } 
    # A client who does not himself have a job, but who has an employee 
    # with a job. 
    let!(:client_with_emp) { create :client } 
    let!(:employee) { create :client_employee, employer: client_with_emp } 
    let!(:emp_job) { create :job, contact: employee } 
    # A client with nothing. Should not show up. 
    let!(:client_without) { create :client } 

    it "should return clients with jobs and clients with employee jobs" do 
    Client.with_commissioned_jobs.should == [client_with, client_with_emp] 
    end 

    it "should return a relation" do 
    Client.with_commissioned_jobs.should be_instance_of(ActiveRecord::Relation) 
    end 
end 
+0

क्या आपने द्वीप में देखा है? https://github.com/rails/arel इसमें प्रश्नों के लिए एक शर्त है, और जटिल संभालती है। – John

+0

मैं निश्चित रूप से एक एरियल आधारित समाधान भी स्वीकार करूंगा। मैंने एक के साथ आने की कोशिश करने में काफी कुछ घंटे बिताए हैं और मुझे लगता है कि मैं इसे प्रबंधित करने में सक्षम नहीं हूं। –

उत्तर

1

आप मणि meta_where माना जाता है? मुख्य बात यह प्रतीत होती है कि आप आगे बढ़ने के लिए ActiveRecord:Relation ऑब्जेक्ट वापस करना चाहते हैं।

अद्यतन 2: यह aliasing

# scope for ::Client 
    def self.with_commissioned_jobs 
    self.joins("LEFT OUTER JOIN client_employees ON clients.id =client_employees.client_id"). 
     joins("LEFT OUTER JOIN jobs AS cjobs ON clients.id = cjobs.contact_id AND cjobs.contact_type = 'Client'"). 
     joins("LEFT OUTER JOIN jobs AS ejobs ON client_employees.id = ejobs.contact_id AND ejobs.contact_type = 'ClientEmployee'"). 
     where("cjobs.id IS NOT NULL OR ejobs.id IS NOT NULL") 
    end 

देखकर अगर यह काम करता है के साथ दो बार LEFT OUTER JOIN नौकरियों के साथ काम कर रहा है:

#c1 has no job 
    c1 = Client.create 

    #c2 has a job 
    c2 = Client.create 
    c2.jobs.create 

    #c3 has no job, but has an employee with a job 
    c3 = Client.create 
    c3.employees.create 
    c3.employees.first.jobs.create 

    puts Client.all.inspect    #=> [#<Client id: 1>, #<Client id: 2>, #<Client id: 3>] 
    puts Client.with_commissioned_jobs #=> [#<Client id: 2>, #<Client id: 3>] 

    puts [c2,c3] == Client.with_commissioned_jobs.all #=> true 
+0

रेल 3.2 का समर्थन नहीं करता मुझे डर है। मैंने [स्क्वाइल] (https://github.com/ernie/squeel) की कोशिश की है जो मेटावेयर के समान है लेकिन मुझे लगता है कि यह 'एआर :: रिलेशनशिप' को मर्ज करने के लिए नहीं मिल रहा है। –

+0

आपका अपडेट एक मामूली संशोधन के साथ काम करता है जिसकी आवश्यकता है क्योंकि मेरे क्लाइंट कर्मचारी विदेशी कुंजी 'customer_id' के विपरीत' employer_id' है। महान काम, धन्यवाद! –

1

इस प्रयास करें:

joins(:jobs, {employees: :jobs}) 

यह ग्राहक की नौकरियों के साथ-साथ ग्राहक कर्मचारियों के रोजगार के अवसर में शामिल होने चाहिए। अधिक विस्तृत जानकारी के लिए the guides देखें।

संपादित

आपके मामले में, आप Relation.merge उपयोग कर सकते हैं:

joins(:jobs).merge(joins(employees: :jobs)) 
+0

मैं मार्गदर्शिकाएं पढ़ रहा हूं (लिंक बीटीडब्ल्यू के लिए धन्यवाद) और जहां तक ​​मैं समझ सकता हूं कि आपके द्वारा प्रदान किए गए कोड को "उन ग्राहकों को लौटाया जाना चाहिए जिनके पास नौकरियां हैं और जिनके पास नौकरियां हैं"। वास्तव में मैं एक या शर्त की तलाश में हूं और मुझे ऐसा करने का कोई तरीका नहीं दिख रहा है। –

+0

मर्ज काम करने के लिए प्रकट नहीं होता है या तो मुझे डर है। मैंने जिन चश्मे के साथ काम कर रहे हैं, उनके साथ-साथ प्रश्न के बारे में अधिक जानकारी दी। –

0
class Client < ActiveRecord::Base 
    has_many :jobs, as: :contact 
    has_many :employees, class_name: 'ClientEmployee' 

    scope :with_commissioned_jobs, lambda do 
    includes(:jobs, {:employees => :jobs}).where("jobs.contact_type IS NOT NULL AND jobs.contact_id IS NOT NULL") 
    end 
end 

ठीक है, असली काम कर आवेदन से मेरी एक और निर्णय। पुराना स्कूल आपकी मदद करता है। :)

यह विधि केवल एआर के लिए सरणीकृत स्थिति बनाएं: पॉलिमॉर्फिक सामान के लिए संबंध।

module ActiveRecordHelper 

    def self.polymorphic_sql(*args) 
    conditions = [] 
    table = args.first.table_name 
    stack = args.extract_options! 
    sql_queries = stack.collect do |as_resource, hash| 
     resource_queries = hash.collect do |name, find_options| 
     resource_class = name.to_s.classify.constantize 
     resource_table = resource_class.table_name 
     conditions << resource_class.name 
     if find_options[:conditions].present? 
      conditions += find_options[:conditions][1..-1] 
     end 
     joins_clause = 
     Array.wrap(find_options[:join]).collect do |association| 
      reflection = resource_class.reflections[association]    
      if reflection.macro == :belongs_to && reflection.options[:polymorphic] != true 
      "INNER JOIN #{reflection.klass.table_name} ON #{reflection.active_record.table_name}.#{reflection.foreign_key} = #{reflection.klass.table_name}.id" 
      elsif reflection.macro.in?([:has_many, :has_one]) && reflection.options[:as].nil? 
      "INNER JOIN #{reflection.klass.table_name} ON #{reflection.klass.table_name}.#{reflection.foreign_key} = #{reflection.active_record.table_name}.id" 
      end 
     end.compact.join(" ").strip 
     "(#{table}.#{as_resource}_type = ? AND EXISTS(#{["SELECT 1 FROM #{resource_table}#{joins_clause.left_indent(1) if joins_clause.present?} WHERE #{resource_table}.id = #{table}.#{as_resource}_id", find_options[:conditions].first].compact.join(" AND ")}))" 
     end 
     "CASE WHEN #{table}.#{as_resource}_type IS NOT NULL AND #{table}.#{as_resource}_id IS NOT NULL THEN #{resource_queries.join(" OR ")} ELSE TRUE END" 
    end 
    conditions.insert(0, "(#{sql_queries.join(" OR ")})") 
    end 

end 

फिर अपने बहुरूपी नौकरी का विस्तार:

def self.comissioned_by(client) 
    conditions = ActiveRecordHelper.polymorphic_sql(self, :contact => {:client => {:conditions => ["clients.id = ?", client.id]}, :client_employee => {:conditions => ["client_employees.client_id = ?", client.id]}} 
    where(conditions) 
end 

अब कॉल करें:

Job.commissioned_by() # pass client instance 

का आनंद लें। यदि कोई विवरण आवश्यक है, तो मुझे टाइप करें।

+0

यह मुझे डरने वाले प्रश्नों में शामिल परीक्षणों पर चलने पर एक खाली संबंध देता है। –

+0

मैं सिर्फ एएसएल शैली वाक्यविन्यास से नफरत करता हूं।) –

1

क्या आपके पास बहुरूपता से चिपकने का एक महत्वपूर्ण कारण है?

यदि कोई क्लाइंट कर्मचारी हमेशा क्लाइंट होता है, तो आपके पास Job.belongs_to :client होना चाहिए। यह आपके संबंध को सरल बनाता है। मैंने पाया है कि कुछ अनावश्यक संघ भी बेहतरीन प्रदर्शन अनुकूलन हो सकते हैं, जब तक कि यह आपके रिकॉर्ड को सुसंगत रखने के लिए और अधिक कठिन नहीं बनाता है (यानी क्लाइंट/क्लाइंट कर्मचारी कर्मचारी जॉब क्लाइंट/जॉब क्लाइंट ई-नियोक्ता असाइनमेंट के साथ समन्वयित होते हैं। दोनों मौजूद हैं)।

मुझे वास्तव में रेल में बहुरूपता पसंद है लेकिन जब आप इस मामले में उनसे जुड़ने की कोशिश कर रहे हैं तो यह मुश्किल हो सकता है। यहां तक ​​कि यदि आपके पास अलग ग्राहक और ग्राहक कर्मचारी आईडी है, तो यह डीबी (दो इंच बनाम int और स्ट्रिंग) में अधिक कुशल होगा।

+0

अच्छा सुझाव। वर्तमान सेटअप का मुद्दा यह है कि यद्यपि केवल ग्राहकों को नौकरी के लिए बिल किया जा सकता है, दिन-प्रति-दिन प्रबंधन में, क्लाइंट के कर्मचारी, ग्राहक के बजाए नौकरी के लिए जाने-माने व्यक्ति हो सकते हैं। मैं क्या देखूँगा क्लाइंट टेबल में क्लाइंट टेबल में रिकॉर्ड होना चाहिए और उसके बाद रोजगार को अनुकरण करने के लिए उस तालिका में आत्म-संदर्भ का उपयोग करना होगा। –

+0

इस मामले में 'def Job.contact; client_employee || ग्राहक; एंड 'प्रदान करता है यह आपकी स्कीमा को आपकी आवश्यकताओं के लिए अनुकूलित रखेगा। – aceofspades

0

क्या आपने कस्टम जुड़ाव करने की कोशिश की है?

def self.with_commissioned_jobs 
    query = <<-QUERY 
    INNER JOIN client_employees 
    ON client_employees.employer_id = clients.id 
    INNER JOIN jobs 
    ON ((jobs.contact_id = client_employees.id AND jobs.contact_type = 'ClientEmployee') 
     OR (jobs.contact_id = clients.id AND jobs.contact_type = 'Client')) 
    QUERY 

    joins(query) 
end 
+0

यह उस क्लाइंट को निकालने के लिए प्रतीत नहीं होता है जो मेरे प्रश्न में शामिल परीक्षणों में सीधे नौकरी का संपर्क है। यह केवल 'client_with_emp' देता है। –

संबंधित मुद्दे