2016-12-08 2 views
1

Ich mache Prüfungen für leere Parameter vor der Abfrage. Es gibt nur einen Haken für params [: car_model_id]. Ich kann mir vorstellen, wenn ich mehr Prüfungen für andere Params hinzufügen werde, dann wird es eine Menge von If-Else-Statements geben. Es sieht nicht gut aus und ich denke, es kann optimiert werden. Aber wie? Hier ist der Code des Steuerpults:Zu viele Prüfungen für leere Parameter. Wie optimiert man Abfragen in ActiveRecord in 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 

Antwort

1

Der Trick wäre, die leeren Werte zu entfernen, führen Sie ein wenig Vorverarbeitung (und möglicherweise Validierung) der Daten, und dann die params übergeben an die where Klausel .

mit der Verarbeitung der Datumsbereiche unterstützen zu können, können Sie eine Methode erstellen, die beide Daten überprüft werden zur Verfügung gestellt und werden auf einen Bereich umgewandelt:

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 

Dann Aktion des Controllers wie folgt aussehen könnte:

# 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) 

auch, ich bin ziemlich sicher, dass der used Wert wird automatisch gegossen bekommen für Sie boolean, wenn seine auf dem where zur Verfügung gestellt.

Auch @cars ist eine ActiveRecord::Relation, die keine errors Methode hat. Vielleicht wollen Sie unterschiedliche Ergebnisse geben, je nachdem, ob Autos zurückgegeben werden?

ZB: @cars.any? (oder @cars.load.any? wenn Sie nicht wollen, zwei Abfragen auszuführen, die Autos zu holen und prüfen, ob Autos vorhanden sind)

Edit:

Wie mu is too short erwähnt können Sie auch sauber up Ihren Code durch Verkettung where Bedingungen und scopes. Bereiche helfen, die Funktionalität aus dem Controller heraus in das Modell zu verschieben, was die Wiederverwendbarkeit der Funktionalität erhöht.

z.

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 

Dann in Ihrem Controller:

# 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

Könnte auch nichts wert sein, dass 'M.where (a: 'b', c: 'd')' ist das gleiche wie 'M.where (a: 'b') .wobei (c: 'd') 'oder sogar 'ms = M.wo (a:' b '); ms = ms.where (c: 'd') 'so könnten Sie die gemeinsamen Teile der' if'- und 'else'-Zweige leicht ausfiltern. Ein wenig flexibler als einen Hash zu manipulieren, da auch Bereiche hinzugefügt werden können. –

+0

Ich stecke bei '' 'car_params.delete''' Debug von Rails Konsole:' '' 2.2.3: 035> car_params [: Jahr] = car_params.delete (: Jahr_von) .. car_params.delete (: year_to) => nil..nil 2.2.3: 037> Auto.where (car_params) Autoladung (0.1ms) SELECT "Autos". * Von "Autos" WO "Autos". "car_model_id" = ? UND "Autos". "Used" =? UND "Autos". "Bedingung" =? UND ("Autos". "Jahr" ZWISCHEN? UND?) UND ("Autos". "Preis" ZWISCHEN? UND?) [["car_model_id", 1], ["gebraucht", wahr], ["bedingung", "perfekt"], ["jahr", nil], ["jahr", nil], ["preis", nil ], ["price", nil]] => # '' ' – Freeminder

+0

Die Anfrage kommt mit leeren Parametern:' '' "year_from" => "", "year_to" => " "," price_from "=>" "," price_to "=>" "' '' And .delete-Methode konvertiert es in '' '..''' – Freeminder

Verwandte Themen