Use in_order_of to sort translations by fallback

This method was introduced in Rails 7.0, and thanks to it we can
simplify the code that gets the translations in order.

We tried to use this method to simplify the `Randomizable` concern as
well. However, we found out that, when ordering tens of thousands of
records, the query could take several minutes, so we aren't using it in
this case. Using it for translation fallbacks is OK, since there's a
good chance we're never going to have tens of thousands of available
locales.

Note that automated security tools reported a false positive related to
SQL Injection due to the way we used `LEFT JOIN`, so now we get one less
false positive in these reports.
This commit is contained in:
Javi Martín
2024-04-06 05:18:01 +02:00
parent a56e1bf3cf
commit 9841a9b03a

View File

@@ -102,17 +102,11 @@ module Globalizable
translations_foreign_key = reflect_on_association(:translations).foreign_key translations_foreign_key = reflect_on_association(:translations).foreign_key
fallbacks = Globalize.fallbacks(Globalize.locale) fallbacks = Globalize.fallbacks(Globalize.locale)
fallbacks_with_order = fallbacks.map.with_index do |locale, order|
"('#{locale}', #{order})"
end.join(", ")
translations_ids = translation_class translations_ids = translation_class
.select("DISTINCT ON (#{translations_foreign_key}) id") .select("DISTINCT ON (#{translations_foreign_key}) id")
.where(locale: fallbacks) .where(locale: fallbacks)
.joins("LEFT JOIN (VALUES #{fallbacks_with_order}) " \ .order(translations_foreign_key)
"AS locales(name, ordering) " \ .in_order_of(:locale, fallbacks)
"ON locale = locales.name")
.order(translations_foreign_key, "locales.ordering")
with_translations(fallbacks).where("#{translations_table_name}.id": translations_ids) with_translations(fallbacks).where("#{translations_table_name}.id": translations_ids)
end end