0

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
4

1 に答える 1