has_many Types テーブルといくつかのスコープを持つ Product モデルを取得しました。
class Product < ActiveRecord::Base
has_many :product_types
has_many :types, through: :product_types
scope :type1, -> { joins(:types).where(types: { name: "Type1" }) }
scope :type2, -> { joins(:types).where(types: { name: "Type2" }) }
end
1 つのスコープ (たとえば Product.type1) を使用しようとするとすべてうまくいきますが、一度に 2 つのスコープ (Product.type1.type2) を使用すると空のクエリが返されます。はい、1 つの製品に複数のタイプがある場合があります。
最終的な目標は、チェックボックス フォームを使用してタイプ別に製品のフィルターを作成することです。type1 と type2 をチェックするとき、Type1 と Type1 を持つすべての製品を同時に表示したいと考えています。
更新 1
そのため、@ aaron.vが提案したように、いくつかのクエリを実行してから & それらを実行しようとしました。関数内でロジックを実行したかったので、次のようにします。
def products_by_type_name(types)
all_types = types.each { |type| Product.joins(:types).where(types: { name: type }).distinct }
...
end
私のポイントは、各タイプを反復処理し、すべての製品を収集してから、それらを関数内に配置することでした。問題は、反復しているときに、各ループがハッシュの配列ではなく文字列を返すことです。
Product.joins(:types).where(types: { name: types }).distinct # returns array of hashes, it's okay.
types.each { |type| Product.joins(:types).where(types: { name: type }).distinct } # each loop returns string (Type name) instead of array of hashes.
私は何を間違っていますか?
解決策 1
@aaron.v による提案、以下で説明
def self.by_type_name(types)
product_ids = []
types.each do |t|
product_ids << (joins(:types).where(types: { name: t }).distinct.select(:id).map(&:id))
end
find(product_ids.inject(:&))
end
解決策 2
redditで見つけました。この関数では、少なくとも 1 つの必須タイプを持つすべての製品をフェッチし、必要な数のタイプを持つ製品のみをグループ化します。したがって、すべてのタイプに属する製品のみを同時に入手できます。
def self.by_type_name(types)
joins(:types).where(types: { name: types }).distinct.group('products.id').having('count(*) = ?', types.each.count)
end