ruby-on-rails MySQL中重构Rails作用域方法CASE-WHEN-THEN-ELSE-END

gr8qqesn  于 2023-03-24  发布在  Ruby
关注(0)|答案(2)|浏览(140)

我正在使用MySQL并将我的应用程序升级到Rails 7。在这样做的过程中,我必须重构我的范围方法中的一些与订单相关的查询。
在Rails 7之前,我使用了以下范围方法(称之为“原始范围方法”):

scope.order(sanitize_sql_array(["CASE articles.author_id WHEN ? THEN 1 ELSE 2 END", author.id]))

在Rails 7中,上面的语句会引发错误:
ActiveRecord::UnknownAttributeReference(使用非属性参数调用的危险查询方法(其参数用作原始SQL的方法):“CASEarticles.author_id WHEN '3784' THEN 1 ELSE 2 END”。不应使用用户提供的值(如请求参数或模型属性)调用此方法。可以通过将已知安全的值 Package 在Arel.sql()中来传递这些值。):
在这些情况下,Rails 7中可能会派上用场的是新添加的方法in_order_of。因此,原始的scope方法可能会变成:

scope.in_order_of(:author_id, [author.id])

然而,这个in_order_of方法向SQL查询添加了一个(unwanted)WHERE子句,即以下查询中的“AND 'articles'.'author_id'”部分:

SELECT `articles`.* FROM `articles` 
WHERE (articles.some_other_column >= 10) AND `articles`.`author_id` IN (3784) 
ORDER BY FIELD(`articles`.`author_id`, 3784) DESC

额外的WHERE子句的(unwanted)效果是从查询结果中删除了所有记录,这些记录是在我使用Rails 7之前的原始范围方法时出现的,即在上面的示例中,除了3784之外的作者ID被忽略,但应该出现在查询结果中。
我如何在Rails 7中构建/重构与原始的“CASE-WHEN-THEN-ELSE-END”等效的范围方法,我在Rails 7之前使用它来根据给定的作者ID对记录进行排序?

s4n0splo

s4n0splo1#

使用Arel调用任何函数都很容易:

scope.order(
  Arel::Nodes::NamedFunction.new('FIELD', [arel_table[:author_id], author.id]).desc
)

Arel::Nodes::NamedFunction.new接受一个函数名和一个参数数组。

9avjhtql

9avjhtql2#

通过按照错误消息中的建议在Arel.sql()中 Package 属性(已知安全的值)找到了解决方案。

scope.order(Arel.sql("FIELD(articles.author_id, #{author.id}) DESC"))

因此SQL查询将是:

SELECT `articles`.* FROM `articles` 
WHERE (articles.some_other_column >= 10)
ORDER BY FIELD(`articles`.`author_id`, 3784) DESC

相关问题