ruby-on-rails - 连接 ActiveRecord::Relations 通过多态关联查询

标签 ruby-on-rails ruby-on-rails-3 activerecord 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

客户有commissioned_jobs的想法.客户委托(delegate)的工作是客户是联系人或客户的一名员工是联系人的那些工作。
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

顺便说一句:该方法有点 hack,因为它返回一个数组而不是 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

我该如何实现这个方法?

我正在使用 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

现在我需要做的就是将这两个方法调用合并为一个 ActiveRecord::Relation .我显然可以这样做:
  def self.with_commissioned_jobs
    with_jobs + with_employee_jobs
  end

问题是它返回一个数组而不是 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'

顺便说一句,这是我试图通过的测试。第一个测试失败,因为我使用合并时结果为零。
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

最佳答案

你考虑过 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

看看它是否有效:
    #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

关于ruby-on-rails - 连接 ActiveRecord::Relations 通过多态关联查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13624240/

相关文章:

ruby-on-rails - globalize3 和 easy_globalize3_accessors 验证

ruby-on-rails - rails 3 : HTTP 406 and blank page on trying to view json in browser

ruby - 事件记录查询

ruby-on-rails - 禁用 ActiveRecord 中的事务

sql - rails : setting column alias attribute with find_by_sql

ruby-on-rails - Rails Assets - NoMethodError `dependency_digest`

ruby-on-rails - 在 Rails 中跟踪 session 时遇到问题

ruby-on-rails - 配置Warden以在RSpec Controller 规范中使用

ruby-on-rails - 如果条件在 rails 3.2 和 mongoid + simple_form 上,则validates_presence_of

ruby-on-rails - rails : Concat Two CollectionProxies with two different models