module Spree module Core module Search class Base attr_accessor :properties, :current_user, :current_currency, :current_store, :taxon def initialize(params) @properties = {} @current_store = params[:current_store] || Spree::Store.default @current_currency = @current_store.default_currency @taxon = params[:taxon] prepare(params) end def retrieve_products @products = extended_base_scope.page(page || 1).per(per_page) end def method_missing(name) if @properties.key? name @properties[name] else super end end protected def extended_base_scope base_scope = current_store.products.spree_base_scopes base_scope = get_products_conditions_for(base_scope, keywords) base_scope = Spree::Dependencies.products_finder.constantize.new(**product_finder_params(base_scope)).execute base_scope = add_search_scopes(base_scope) add_eagerload_scopes(base_scope) end def product_finder_params(base_scope) { scope: base_scope, params: { store: current_store, filter: { price: price, option_value_ids: option_value_ids, properties: product_properties, taxons: taxon&.id, currency: current_currency }, sort_by: sort_by } } end def add_eagerload_scopes(scope) scope.includes( :tax_category, variants: [ { images: { attachment_attachment: :blob } } ], master: [ :prices, { images: { attachment_attachment: :blob } } ] ) end def add_search_scopes(base_scope) if search.is_a?(ActionController::Parameters) search.each do |name, scope_attribute| scope_name = name.to_sym base_scope = if base_scope.respond_to?(:search_scopes) && base_scope.search_scopes.include?(scope_name.to_sym) base_scope.send(scope_name, *scope_attribute) else base_scope.merge(Spree::Product.ransack(scope_name => scope_attribute).result) end end end base_scope end # method should return new scope based on base_scope def get_products_conditions_for(base_scope, query) unless query.blank? base_scope = base_scope.i18n { name.matches("%#{query}%").or(description.matches("%#{query}%")) } end base_scope end def get_products_option_values_conditions(base_scope, option_value_ids) unless option_value_ids.blank? base_scope = base_scope.joins(variants: :option_values).where(spree_option_values: { id: option_value_ids }) end base_scope end def get_price_range(price_param) return if price_param.blank? less_than_string = I18n.t('activerecord.attributes.spree/product.less_than') if price_param.include? less_than_string low_price = 0 high_price = Monetize.parse(price_param.remove("#{less_than_string} ")).to_i else low_price, high_price = Monetize.parse_collection(price_param).map(&:to_i) high_price = BigDecimal::INFINITY if high_price&.zero? end "#{low_price},#{high_price}" end def build_option_value_ids(params) filter_params = Spree::OptionType.filterable.map(&:filter_param) filter_params.reduce([]) do |acc, filter_param| acc + params[filter_param].to_s.split(',') end end def prepare(params) @properties[:keywords] = params[:keywords] @properties[:option_value_ids] = build_option_value_ids(params) @properties[:price] = get_price_range(params[:price]) @properties[:search] = params[:search] @properties[:sort_by] = params[:sort_by] || 'default' @properties[:include_images] = params[:include_images] per_page = params[:per_page].to_i @properties[:per_page] = per_page > 0 ? per_page : Spree::Config[:products_per_page] @properties[:page] = if params[:page].respond_to?(:to_i) params[:page].to_i <= 0 ? 1 : params[:page].to_i else 1 end @properties[:product_properties] = params[:properties] end end end end end