9

私のアプリケーションにはジョブモデルがあります。システム内のすべてのジョブにはがありcontactます。これは、仕事について質問する必要がある場合に呼び出す人のようなものです。連絡先はclient、クライアントのaまたは従業員のいずれかになります(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ます。クライアントに委託されたジョブは、クライアントが連絡先であるか、クライアントの従業員の1人が連絡先であるジョブです。

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。ジョブを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

このメソッドを実装するにはどうすればよいですか?

Rails3.2.9を使用しています。

アップデート:

私はある程度の進歩を遂げましたが、今では2つの方法があり、それぞれが必要な方法の半分を実行します。

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

今私がする必要があるのは、これら2つのメソッド呼び出しを1つに結合することです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
4

5 に答える 5

1

gem meta_whereを検討しましたか? ActiveRecord:Relation主なことは、さらにチェーンするためにオブジェクトを返したいということです。

更新 2 :LEFT OUTER JOINエイリアシングを使用してジョブを 2 回処理するようになりました

  # 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
于 2012-12-10T18:15:42.283 に答える
1

これを試して :

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

クライアントの仕事だけでなく、クライアントの従業員の仕事にも参加する必要があります。詳細については、ガイドを参照してください。

編集

あなたの場合、次を使用できますRelation.merge

 joins( :jobs ).merge( joins(employees: :jobs) )
于 2012-11-29T12:38:02.463 に答える
1

ポリモーフィズムに固執する重要な理由はありますか?

ClientEmployee に常に Client がある場合は、おそらくJob.belongs_to :client. これにより、関係が非常にシンプルになります。レコードの一貫性を維持することがより困難にならない限り (つまり、次の場合に Client/ClientEmployee の関係が Job.Client/Job.ClientEmployee の割り当てと同期している場合)、冗長な関連付けを追加することでパフォーマンスを最適化することもできます。どちらも存在します)。

私はレールでのポリモーフィズムが本当に好きですが、この場合のようにレールを越えて結合しようとすると、扱いにくくなる可能性があります。Client ID と ClientEmployee ID を別々に持っていたとしても、データベースではより効率的です (2 つの int と int および string)。

于 2012-12-11T18:20:09.453 に答える
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

わかりました、実際の作業アプリケーションからの私の別の決定。古い学校はあなたを助けます。:)

このメソッドは、多態的なものの AR:Relation の配列条件を作成するだけです。

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

楽しみ。詳細が必要な場合は、私と入力してください。

于 2012-12-10T22:56:16.677 に答える
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 
于 2012-12-11T19:17:48.863 に答える