2013-03-06 2 views
2

Как я могу реорганизовать этот код? Есть ли способ разделить предложение where, функции include и 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 для лучшего способа), чтобы DRY-часть кода и сделать ее более читаемой.

Мой пример выше по-прежнему нуждается в большой работе, но я думаю, вы можете собрать хороший код, следуя этому шаблону.

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 в предложении заказа, рельсы хорош в защите, где положение, но не порядок, см rails 3 activerecord order - what is the proper sql injection work around?

2) Это больше не создают тот же SQL, как ваш запрос, но я предполагаю, что результат такой же, рельсы AREL где СЦЕПЛЕНИЕ всегда будет делать AND xxxxx добавление OR может быть сложнее, но в вашем примере кода оказалось, что OR ean ilike :q находится в каждом из этих запросов, и никаких круглых скобок не используется, поэтому я помещаю i в ядро, возможно, вы действительно хотите скобки и другой результат, не могли понять, почему AND console_id = :c дважды появляется в некоторых из этих запросов.

Смежные вопросы