2013-03-06 30 views
2

我該如何重構這段代碼?有沒有辦法拆分where子句,包含和order函數?如何重構長條款的地方?

def self.product_search(query, console, genre, sort, order) 
     if query 
      #search(query) 
      if !console.nil? && console != "all" && !genre.nil? && genre != "all" 
       where("name_en ilike :q AND console_id = :c AND genre_id = :g OR ean ilike :q AND console_id = :c AND genre_id = :g", q: "%#{query}%", c: console, g: genre).includes(:genre, :console, :brand, :images).order("#{sort} #{order}") 
      elsif !console.nil? && console != "all" 
       where("name_en ilike :q AND console_id = :c OR ean ilike :q AND console_id = :c", q: "%#{query}%", c: console).includes(:genre, :console, :brand, :images).order("#{sort} #{order}") 
       elsif !genre.nil? && genre != "all" 
       where("name_en ilike :q AND genre_id = :g OR ean ilike :q AND genre_id = :g", q: "%#{query}%", g: genre).includes(:genre, :console, :brand, :images).order("#{sort} #{order}") 
      else 
       where("name_en ilike :q OR ean ilike :q", q: "%#{query}%").includes(:genre, :console, :brand, :images).order("#{sort} #{order}") 
      end 
     end 
end 

回答

1

你可以用碎片構建AREL表達式;它們只有在迭代或使用時纔會執行。例如,你可以做這樣的事情:

def self.product_search(query, console, genre, sort, order) 
    if query 
    clause = all # Start with all, filter down. 
    if !console.nil? && console != "all" && !genre.nil? && genre != "all" 
     clause = clause.where("name_en ilike :q AND console_id = :c AND genre_id = :g OR ean ilike :q AND console_id = :c AND genre_id = :g", q: "%#{query}%", c: console, g: genre) 
    elsif !console.nil? && console != "all" 
     clause = clause.where("name_en ilike :q AND console_id = :c OR ean ilike :q AND console_id = :c", q: "%#{query}%", c: console) 
    elsif !genre.nil? && genre != "all" 
     clause = clause.where("name_en ilike :q AND genre_id = :g OR ean ilike :q AND genre_id = :g", q: "%#{query}%", g: genre) 
    else 
     clause = clause.where("name_en ilike :q OR ean ilike :q", q: "%#{query}%") 
    end 
    clause.includes(:genre, :console, :brand, :images).order("#{sort} #{order}") 
    end 
end 

你可以把鏈接和分配,直到你建立你想整個搜索條款。這可以進一步優化,但我認爲這足以證明鏈接AREL表達式的要點。

您也可以拋棄很多那些nil檢查,如果你扭轉一些邏輯和檢查console.nil?genre.nil?第一,然後在else條款,只是檢查genre == "all",例如。

也可以將其中的一些定義爲模型上的命名範圍(或者參閱此博客文章,名爲Named Scopes Are Dead以獲得更好的方式),以乾燥某些代碼並使其更具可讀性。

我上面的例子仍然需要很多工作,但我認爲你可以按照這種模式組裝一些漂亮的代碼。

0

這可能是服用的東西遠你,但我願意代碼轉移到另一個物體

# code in Product model 
def self.product_search(search_criteria, console, genre, sort, order) 
    return nil unless search_criteria.present? 
    ProductSearch.new(search_criteria, genre, sort, order).find 
end 

# new class to handle Product search 
class ProductSearch 
    def initialize(search_criteria, console, genre, sort, order) 
    @search_criteria = search_criteria 
    @console = console 
    @genre = genre 
    @sort = sort 
    @order = order 
    end 

    attr_reader :search_criteria, :console, :genre, :sort, :order 

    def core_query_for_product_search 
    # WARNING: .order("#{sort} #{order}") is open to sql injection attacks 
    self.includes(:genre, :console, :brand, :images) 
     .order("#{sort} #{order}") 
     .where("name_en ilike :q OR ean ilike :q", q: "%#{search_criteria}%") 
    end 

    def with_console? 
    !console.nil? && console != "all" 
    end 

    def with_genre? 
    !genre.nil? && genre != "all" # you might want genre.present? instead of !genre.nil? 
    end 

    def find 
    query = core_query_for_product_search 
    query = query.where("genre_id = :g", g: genre) if with_genre? 
    query = query.where("console_id = :c", c: console) if with_console? 

    query 
    end 
end 

幾件事情需要注意:

1)SQL注入的順序子句中,軌道善於保護where子句,但不是爲了見rails 3 activerecord order - what is the proper sql injection work around?

2)此不再創建完全相同的SQL的查詢,但我猜的結果是一樣的,導軌AREL其中鏈接會一直做AND xxxxx增加OR正確可以更困難,但在您的示例代碼中,似乎OR ean ilike :q是在每個查詢中,並且沒有使用括號,所以我把我放在覈心,也許你實際上想要括號和不同的結果,不能理解爲什麼AND console_id = :c在某些查詢中出現兩次