Viele Wege führen nach Rom. So auch bei Abfragen in Rails. Es gibt 3 verschiedene Wege, um Konditionen zu formulieren:
- String
- Array
- Hash
[code lang=“ruby“]class User < ActiveRecord::Base
def self.authenticate_unsafely(user_name, password)
where("user_name = ‚#{user_name}‘ AND password = ‚#{password}’").first
end
def self.authenticate_safely(user_name, password)
where("user_name = ? AND password = ?", user_name, password).first
end
def self.authenticate_safely_simply(user_name, password)
where(:user_name => user_name, :password => password).first
end
end[/code]
Die String-Methode ist unsicher, da die Daten nicht bereinigt werden und so SQL-Injections möglich sind.
Die Array-Methode ist sicher und erinnert syntaktisch stark an Prepared Statements.
Die Hash-Methode ist ebenfalls sicher, aber typisch Rails, weil einfach: lediglich ein Hash wird übergeben. Der Schlüssel (key) als Symbol entspricht der Spaltenbezeichnungen der Tabelle und der Wert (value) definiert die Bedingung.
Das Schöne an der Hash-Methode ist neben der Sicherheit, dass das Hash wesentlich einfacher dynamisch berechnet werden kann (als die Array-Methode):
[code lang=“ruby“]def all_by_employee_or_manager(employee_id=nil, manager_id=nil)
condition = Hash.new
condition[:employee_id] = employee_id unless employee_id.blank?
condition[:manager_id] = manager_id unless manager_id.blank?
where(condition)
end[/code]
Das Problem: NoMethodError
Sobald jedoch in der Abfrage gejoint wird, bekommt man bei einer Hash-Bedingung wie
[code lang=“ruby“]joins(:employees).where(:employees.name => name)[/code]
folgenden Fehler:
[code]NoMethodError: undefined method `name‘ for :employees:Symbol[/code]
Das Problem ist, dass das Symbol :employees.name nicht komplett als Symbol interpretiert wird, sondern nur bis zum Punkt.
Bei der Internetrecherche habe ich für gejointe Abfragen nur Konditionen im String- oder Array-Format finden können, was in meinem Anwendungsfall aber nicht praktikabel war, denn die Konditionen mussten dynamisch berechnet werden.
Die Lösung
Um Konditionen in gejointen Abfragen mit der Hash-Methode anzugeben, muss Ruby klar gemacht werden, dass employees.name komplett als Symbol verstanden werden soll. Das geht so:
[code lang=“ruby“]joins(:employees).where(:"employees.name" => name)[/code]
Einfach, wenn gewusst wie!