Dav*_*ite 8 activerecord ruby-on-rails ruby-on-rails-3 ruby-on-rails-3.2
我的应用程序有一个Job模型.系统中的每个工作都有一个contact.如果您需要询问有关工作的问题,这就像您打电话的人.联系人可以是client客户(或)的雇员(ClientEmployee).
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
Run Code Online (Sandbox Code Playgroud)
客户有这个想法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
Run Code Online (Sandbox Code Playgroud)
旁白:这个方法有点像黑客,因为它返回一个数组而不是一个数组ActiveRecord::Relation.有趣的是,如果我尝试将作业连接到employee_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
Run Code Online (Sandbox Code Playgroud)
我该如何实现这个方法?
我正在使用Rails 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
Run Code Online (Sandbox Code Playgroud)
现在我需要做的就是将这两个方法调用合二为一ActiveRecord::Relation.我显然可以这样做:
def self.with_commissioned_jobs
with_jobs + with_employee_jobs
end
Run Code Online (Sandbox Code Playgroud)
问题是返回一个数组而不是一个实例,Relation我无法在其上链接更多的范围.
更新2:
使用merge似乎也不起作用.这是AR查询和结果SQL.
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'
Run Code Online (Sandbox Code Playgroud)
顺便说一句,这是我试图通过的测试.第一次测试失败,因为当我使用merge时没有结果.
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
Run Code Online (Sandbox Code Playgroud)
您是否考虑过 gem meta_where?主要的事情似乎是您想要返回一个ActiveRecord:Relation对象以进行进一步的链接。
更新2:让它与LEFT OUTER JOIN工作一起工作两次并使用别名
# 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
Run Code Online (Sandbox Code Playgroud)
看看是否有效:
#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
Run Code Online (Sandbox Code Playgroud)