首先,您应该考虑使用 rails/activerecord 符合关系。这意味着 text_translations 表中的外键应称为 text_id
像这样创建模型和关联:
class Text < ActiveRecord::Base
# all possible translations!
has_many :text_translations
scope :with_translation_for, lambda { |lang| {
:select => "texts.*, tt.translation",
:joins => "LEFT OUTER JOIN text_translations AS tt ON tt.text_id = texts.id AND tt.locale = #{ActiveRecord::Base.sanitize(lang)}"
}}
# return nil if translation hasn't been loaded, otherwise you get a nasty NoMethod exception
def translation
read_attribute(:translation)
end
end
和
class TextTranslation < ActiveRecord::Base
# every translation belongs to a text
belongs_to :text
# define a scope for the language
scope :language, lambda { |lang| where(['locale = ?', lang]) }
end
如何使用:
texts = Text.with_translation_for('en')
texts.each do |c_text|
unless c_text.translation.nil?
puts c_text.translation
else
puts "No translation available!"
end
end
现在来看看利弊,即使没有所需语言的文本翻译,使用 LEFT OUTER 连接的方式也会加载所有文本。缺点是您不会获得“TextTranslation”模型对象。
另一种方法是仅加载具有所需翻译的文本。你可以这样做:
texts = Text.includes(:text_translations).where(:text_translations => {:locale => 'en'})
现在texts[i].text_translations
将返回一个数组,其中包含与区域设置“en”匹配的此文本的所有 TextTranslations 模型对象。但是在区域设置“en”中没有翻译的文本不会显示。
编辑
连接到您的评论:
在关系上使用的问题.join(:tablename)
是,它会导致 INNER JOIN,所以这不是一个选项。您必须明确声明左连接。另一件事是,如果您使用类似Text.includes(:text_translations).where(['text_translations.locale = ?', 'en'])
条件的东西,则将整体应用于 SQL 查询,而不是可能的 LEFT 连接本身。您实际上可以做的是声明关联,例如
has_many :english_translations, :class_name => 'TextTranslation', :conditions => ['locale = ?', 'en']
这样,您可以通过急切加载(根本没有任何连接)来管理仅加载英文翻译:
Text.includes(:english_translations).all
检查一下: