我正在尝试为Article类创建一个过滤器,该过滤器使用基于HTML表单提交的参数动态构建的SQL查询。由于我有多个多对多的关系,我不能使用Article.where(我不认为反正)。虽然以下代码有效,但我不确定这是否是执行此查询的最有效方法,以及它是多么安全。我确实通过使用?来防止SQL注入? sql字符串中的关键字(la Rails约定),但想确保这就足够了。关于如何让这更优雅的任何建议?
def self.filter(hash)
hash.delete_if {|k,v| v == ""}
hash[:writer_type] = (hash[:writer_type]) if hash[:writer_type] != nil
sql_base = "select distinct articles.* from articles
join tags
on tags.article_id = articles.id
join categories
on tags.category_id = categories.id
left outer join itineraries
on itineraries.article_id = articles.id
left outer join cities
on itineraries.city_id = cities.id
join users
on users.id = articles.user_id"
condition_array = []
key_array = []
hash.each_key {|key| key_array << key}
key_array.each_with_index do |key, i|
operator = "and"
operator = "where" if i == 0
case key
when :writer
sql_base << "\n#{operator} users.username like ?"
condition_array << hash[:writer]
when :writer_type
sql_base << "\n#{operator} users.status in (?)"
condition_array << hash[:writer_type]
when :city
sql_base << "\n#{operator} cities.name like ?"
condition_array << hash[:city]
when :category
sql_base << "\n#{operator} categories.name like ?"
condition_array << hash[:category]
end
end
sql_array = [sql_base,condition_array].flatten
articles = Article.find_by_sql(sql_array)
articles
end
答案 0 :(得分:1)
当然,你应该可以这样做:
q = Articles.join(:tags #, etc)
if condition
q = q.joins(:user).where("users.status in ?", hash[:writer_type])
else
q = q.joins(:cities).where("cities.name LIKE ?", hash[:city])
end
q
这是有效的,因为ActiveRecord::Relation
仅在首次访问时执行查询。因此,在您通过记录调用to_a
或枚举之前,您可以继续链接到对象上。
如果您正在进行复杂的查询,可能需要查看squeel它会让您重写条件,如
q.where { users.status >> my { hash[:writer_type] } }
或
q.where { cities.name =~ my { hash[:city] } }