2016-12-08 9 views
1

Я делаю проверки для пустых параметров перед выполнением запроса. Существует только 1 проверка для params [: car_model_id]. Я могу себе представить, добавлю ли я дополнительные проверки для других параметров, тогда будет беспорядок операторов if-else. Это не выглядит красиво, и я думаю, что его можно оптимизировать. Но как? Вот код контроллера:Слишком много проверок для пустых параметров. Как оптимизировать запросы к ActiveRecord в Rails5?

class CarsController < ApplicationController 
def search 
    if params[:car_model_id].empty? 
    @cars = Car.where(
     used: ActiveRecord::Type::Boolean.new.cast(params[:used]), 
     year: params[:year_from]..params[:year_to], 
     price: params[:price_from]..params[:price_to], 
     condition: params[:condition] 
    ) 
    else 
    @cars = Car.where(
     used: ActiveRecord::Type::Boolean.new.cast(params[:used]), 
     car_model_id: params[:car_model_id], 
     year: params[:year_from]..params[:year_to], 
     price: params[:price_from]..params[:price_to], 
     condition: params[:condition] 
    ) 
    end 

    if @cars 
    render json: @cars 
    else 
    render json: @cars.errors, status: :unprocessable_entity 
    end 
end 
end 

ответ

1

Трюк было бы удалить пустые значения, сделать немного предварительной обработки (и, возможно, валидация) данных, а затем передать Params к статье where ,

Чтобы помочь с обработкой диапазонов дат, вы можете создать метод, который проверяет обе даты предусмотрены и преобразуются в диапазоне:

def convert_to_range(start_date, end_date) 
    if start_date && end_date 
    price_from = Date.parse(price_from) 
    price_to = Date.parse(price_to) 
    price_from..price_to 
    end 
rescue ArgumentError => e 
    # If you're code reaches here then the user has invalid date and you  
    # need to work out how to handle this. 
end 

Тогда ваше действие контроллера может выглядеть примерно так:

# select only the params that are need 
car_params = params.slice(:car_model_id, :used, :year_from, :year_to, :price_from, :price_to, :condition) 

# do some processing of the data 
year_from = car_params.delete(:year_from).presence 
year_to = car_params.delete(:year_to).presence 
car_params[:price] = convert_to_range(year_from, year_to) 

price_from = car_params.delete(:price_from).presence 
price_to = car_params.delete(:price_to).presence 
car_params[:price] = convert_to_range(price_from, price_to) 

# select only params that are present 
car_params = car_params.select {|k, v| v.present? } 

# search for the cars 
@cars = Car.where(car_params) 

Кроме того, я уверен, что значение used будет автоматически приводиться к булева для вас, когда его предоставлена ​​where.

Также @cars является ActiveRecord::Relation, который не имеет метода errors. Возможно, вы хотите дать разные результаты в зависимости от того, вернулись ли какие-либо автомобили?

Например: @cars.any? (или @cars.load.any?, если вы не хотите, чтобы выполнить два запроса, чтобы забрать машины и проверить, существуют ли автомобили)

Edit:

Как упомянуто mu is too short вы можете также чистые вверх по вашему коду путем цепочки where условий и scopes. Области применения помогают перемещать функциональность из контроллера и в модель, что увеличивает возможность повторного использования функциональности.

E.g.

class Car > ActiveRecord::Base 

    scope :year_between, ->(from, to) { where(year: from..to) } 
    scope :price_between, ->(from, to) { where(price: from..to) } 

    scope :used, ->(value = true) { where(used: used) } 

end 

Тогда в контроллере:

# initial condition is all cars 
cars = Cars.all 

# refine results with params provided by user 
cars = cars.where(car_model_id: params[:car_model_id]) if params[:car_model_id].present? 
cars = cars.year_between(params[:year_from], params[:year_to]) 
cars = cars.price_between(params[:price_from], params[:price_to]) 
cars = cars.used(params[:used]) 
cars = cars.where(condition: params[:condition]) if params[:condition].present? 

@cars = cars 
+1

не Может быть стоит ничего, что 'M.where (а: 'B', C: 'd')' является такой же, как 'M.where (a: 'b'). где (c: 'd') 'или даже' ms = M.where (a: 'b'); ms = ms.where (c: 'd') ', поэтому вы можете достаточно легко разделить общие части ветвей' if' и 'else'. Немного более гибкий, чем манипулирование хешем, так как вы можете бросить в области. –

+0

Я застрял в '' 'car_params.delete''' Отладка с консоли rails:' '' 2.2.3: 035> car_params [: year] = car_params.delete (: year_from) .. car_params.delete (: year_to) => nil..nil 2.2.3: 037> Car.where (car_params) Автомобильная нагрузка (0,1 мс) ВЫБРАТЬ «автомобили». * ОТ «Автомобили» ГДЕ «автомобили». «car_model_id» = ? И "автомобили". "Used" =? И "автомобили". "Condition" =? И («автомобили». «Год» МЕЖДУ И?) И («автомобили». «Цена» МЕЖДУ И?) [[car_model_id ", 1], [" used ", true], [" condition "," perfect "], [" year ", nil], [" year ", nil], [" price ", nil ], ["price", nil]] => # '' ' – Freeminder

+0

Запрос поставляется с пустыми параметрами:' '' "year_from" => "", "year_to" => "," price_from "=>" "," price_to "=>" "' '' И метод .delete преобразует его в '' '' '''' – Freeminder