module Spree module Products class Find def initialize(scope:, params:, current_currency:) @scope = scope @ids = String(params.dig(:filter, :ids)).split(',') @skus = String(params.dig(:filter, :skus)).split(',') @price = String(params.dig(:filter, :price)).split(',').map(&:to_f) @currency = params[:currency] || current_currency @taxons = String(params.dig(:filter, :taxons)).split(',') @name = params.dig(:filter, :name) @options = params.dig(:filter, :options).try(:to_unsafe_hash) @option_value_ids = params.dig(:filter, :option_value_ids) @sort_by = params.dig(:sort_by) @deleted = params.dig(:filter, :show_deleted) @discontinued = params.dig(:filter, :show_discontinued) end def execute products = by_ids(scope) products = by_skus(products) products = by_price(products) products = by_taxons(products) products = by_name(products) products = by_options(products) products = by_option_value_ids(products) products = include_deleted(products) products = include_discontinued(products) products = ordered(products) products end private attr_reader :ids, :skus, :price, :currency, :taxons, :name, :options, :option_value_ids, :scope, :sort_by, :deleted, :discontinued def ids? ids.present? end def skus? skus.present? end def price? price.present? end def taxons? taxons.present? end def name? name.present? end def options? options.present? end def option_value_ids? option_value_ids.present? end def sort_by? sort_by.present? end def name_matcher Spree::Product.arel_table[:name].matches("%#{name}%") end def by_ids(products) return products unless ids? products.where(id: ids) end def by_skus(products) return products unless skus? products.joins(:variants_including_master).distinct.where(spree_variants: { sku: skus }) end def by_price(products) return products unless price? products.joins(master: :default_price). distinct. where( spree_prices: { amount: price.min..price.max, currency: currency } ) end def by_taxons(products) return products unless taxons? products.joins(:taxons).distinct.where(spree_taxons: { id: taxons }) end def by_name(products) return products unless name? products.where(name_matcher) end def by_options(products) return products unless options? products.where( id: options.map do |key, value| products.with_option_value(key, value).ids end.flatten.compact.uniq ) end def by_option_value_ids(products) return products unless option_value_ids? products.joins(variants: :option_values).distinct.where(spree_option_values: { id: option_value_ids }) end def ordered(products) return products unless sort_by? case sort_by when 'default' products when 'newest-first' products.order(available_on: :desc) when 'price-high-to-low' products.select('spree_products.*, spree_prices.amount').reorder('').send(:descend_by_master_price) when 'price-low-to-high' products.select('spree_products.*, spree_prices.amount').reorder('').send(:ascend_by_master_price) end end def include_deleted(products) deleted ? products.with_deleted : products.not_deleted end def include_discontinued(products) discontinued ? products : products.available end end end end