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 查询中一样。
我有一些模型要显示在分页列表(使用 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 查询中一样。