core/lib/spree/core/search/base.rb

Summary

Maintainability
A
1 hr
Test Coverage
B
81%
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