Rails 通过带有回退和 will_paginate 的已翻译列对对象进行全球化排序

Rails Globalize sort objects by translated column with fallbacks and will_paginate

我有一些模型要显示在分页列表(使用 will_paginate)中,按使用 Globalize 翻译的列排序。

要求说明必须可以用任何语言创建对象,并且如果当前语言环境没有翻译可用,则按预定义的顺序回退到任何其他语言。

我 运行 遇到的问题是,如果我在翻译 table 上加入 table,并且翻译是混合的,will_paginate 会失败,因为即使通过不同的调用,#count 计算错误并且 AR 关系上的#limit 无法按预期工作。

例如:

我有一个 Exhibitor 模型,它具有来自 Globalize 的相应 Exhibitor::Translation,并且 Exhibitor 对象可以在任何或所有配置的语言环境中进行翻译。

Exhibitor.with_translations.order(:sort_string).limit(2) 

returns 只有一个对象,因为第一个 Exhibitor 对象有 2 个翻译,甚至 #distinct 调用也不会改变这一点,这意味着 will_paginate 变得混乱并且

我想我想要的是这样的:

SELECT exhibitors.id,translations.sort_string
FROM exhibitors
INNER JOIN
exhibitor_translations translations ON translations.exhibitor_id = 
exhibitors.id
WHERE translations.locale = 'en' OR translations.locale = 'de' OR 
translations.locale = 'fr'
ORDER BY translations.sort_string;

WHERE 部分是我苦苦挣扎的地方,因为我只想要存在的第一个翻译,但在这里我要找回每个对象的所有可用内容。

我希望这是一个可以理解的解释,我仍在努力在脑海中准确地表述它,所以如果需要任何说明,请直接提问。

我在这里尝试了这个解决方案的几个变体 Globalize3 order records by translated attributes and taking fallbacks into consideration

但是 will_paginate 仍然显示错误的计数并且不显示导航链接。

如果有人遇到同样的问题,我终于用这样的子查询解决了它:

SELECT  DISTINCT
    COALESCE(b.exhibitor_id, c.exhibitor_id, a.exhibitor_id) exhibitor_id,
    COALESCE(b.sort_string, c.sort_string, a.sort_string) sort_string

FROM exhibitor_translations a
   LEFT JOIN
    (
        SELECT ID, exhibitor_id, sort_string, locale
        FROM exhibitor_translations
        WHERE locale = 'fr'
     ) b ON a.exhibitor_id = b.exhibitor_id
   LEFT JOIN
    (
        SELECT ID, exhibitor_id, sort_string, locale
        FROM exhibitor_translations
        WHERE locale = 'en'
     ) c ON a.exhibitor_id = c.exhibitor_id; 

Rails 代码如下所示(不是最终的生产代码,但足够接近):

    entity            = Exhibitor
    query             = entity.all
    translation_table = entity.translations_table_name
    foreign_key       = "#{entity.name.demodulize.underscore}_id"
    attribute_name    = "sort_string"

    subquery = entity.translation_class.select("
      DISTINCT
      COALESCE(b.id, a.id) id,
      COALESCE(b.#{foreign_key}, a.#{foreign_key}) #{foreign_key},
      COALESCE(b.#{attribute_name}, a.#{attribute_name}) #{attribute_name}
    ")
    subquery.from("#{translation_table} AS a")
    subquery = subquery.joins("
      LEFT JOIN
      (
        SELECT id, #{foreign_key}, #{attribute_name}
        FROM #{translation_table}
        WHERE locale = '#{I18n.locale}'
      ) b ON a.id <> b.id AND a.#{foreign_key} = b.#{foreign_key}
    ")

    query = query.joins("INNER JOIN (#{subquery.to_sql}) t ON #{entity.table_name}.id = t.#{foreign_key}")

    query.order("t.#{attribute_name} ASC")

如果有人想使用它,请注意 SQL 注入可能性并使用 ActiveRecord#quote 获取外部值(我目前没有任何用户输入此查询)

对于 > 2 个语言环境,使用多个连接子句,就像在上面的原始 sql 查询中一样。