6

Sto implementando una semplice funzione di ricerca che dovrebbe verificare una stringa nel nome utente, nel cognome e nel nome. Ho visto questo metodo ActiveRecord su un vecchio Railscast:Rails ActiveRecord - Ricerca su più attributi

http://railscasts.com/episodes/37-simple-search-form

find(:all, :conditions => ['name LIKE ?', "%#{search}%"]) 

Ma come faccio a fare in modo che esso cerca la parola chiave nel nome, cognome e nome e restituisce il record se il uno dei campi corrisponde al termine?

Mi chiedo anche se il codice sul RailsCast è soggetto a iniezioni SQL?

Grazie mille!

risposta

20

ho assunto il nome del modello è il modello - basta sostituirlo con il nome del modello vero quando si esegue la query effettiva:

Model.where("name LIKE ? OR last_name LIKE ? OR first_name LIKE ?", "%#{search}%","%#{search}%","%#{search}%") 

le vostre preoccupazioni circa iniezioni SQL - entrambi frammenti di codice sono immuni iniezioni SQL . Finché non incorpori direttamente le stringhe nella tua clausola WHERE, stai bene. Un esempio per il codice di iniezione-prone sarebbe:

Model.where("name LIKE '#{params[:name]}'") 
+0

Proprio quello di cui avevo bisogno. Grazie per averlo spiegato chiaramente! – maru

+0

Cosa succede se il controller viene modificato per passare l'intero 'params' anziché solo' params [: search] 'e nella nostra query usiamo' "% # {params [: search]}%" 'invece di' "% # {search}% "'. È ancora immune alle iniezioni SQL? – Dennis

+0

@Dennis si lo è. Finché stai usando? e passando argomento extra per sostituirli nella query stai bene. –

8

Anche se la risposta selezionata funzionerà, ho notato che si rompe se provate a digitare una ricerca "Raul Riera" perché avrà esito negativo su entrambi i casi, perchè Raul Riera non è neanche il mio nome o il mio cognome .. è il mio nome e cognome ... ho risolto facendo

Model.where("lower(first_name || ' ' || last_name) LIKE ?", "%#{search.downcase}%") 
+0

Ho trovato questa risposta più utile. Grazie! –

+0

Non sarebbe meglio usare 'ILIKE' piuttosto che ridurlo? Lascia che sia il DB a gestirlo. –

+0

Non funziona quando last_name o first_name è uguale a nil, in tal caso, è necessario impostare il valore di stringa vuoto predefinito sul nome e sul cognome –

0

Il modo migliore per farlo è:

Model.where("attr_a ILIKE :query OR attr_b ILIKE :query", query: "%#{query}%") 
0

Con Arel, puoi evitare di scrivere SQL manualmente con qualcosa del genere:

Model.where(
    %i(name first_name last_name) 
    .map { |field| Model.arel_table[field].matches("%#{query}%") 
    .inject(:or) 
) 

Ciò sarebbe particolarmente utile se l'elenco di campi da confrontare era dinamico.

Problemi correlati