上下文
我有五个模型:奖励,BrandSubscription,Brand,Tier和User。Brand
有许多Tiers
。BrandSubscription
属于Tier
和User
。Reward
属于Tier
。Tier
具有一个名为order
的属性。如果BrandSubscription
具有较高的层,则它也将具有所有较低的层。BrandSubscription
可以查看其所有Rewards
中的所有Tiers
,在这种情况下,即为其所属的Tier
及其所有下级Tiers
。
问题
我的问题是上面列表的最后一项。我正在尝试获得品牌订阅的所有奖励。
理想的方式是has_many :rewards
实体上的BrandSubscription
,即through: :tier
。在Tier
上我可以有一个has_many :rewards
。这种方法的问题在于,奖励不仅限于当前级别,还必须包括来自较低order
级别的所有奖励。
为此,我将范围放在has_many :rewards
模型的Tier
上:
class Tier < ActiveRecord::Base
belongs_to :brand
has_many :rewards
has_many :with_lower_tiers, lambda {
where(this_table[:order].gteq(that_table[:order]))
}, through: :brand, source: :tiers
has_many :achievable_rewards, through: :with_lower_tiers, source: rewards
end
这里的问题是
this_table
和that_table
。我需要在这里进行某种联接,以便可以在表之间进行比较。我可以工作的一种方法是:class Tier < ActiveRecord::Base
belongs_to :brand
has_many :rewards
has_many :with_lower_tiers, lambda { |tier|
where(current_scope.table[:order].lteq(tier.order))
}, through: :brand, source: :tiers
has_many :achievable_rewards, through: :with_lower_tiers, source: rewards
end
在这里,我使用层所有者对象并获取其顺序。这里的问题是我不能真正依靠
tier
参数。 follow查询已经中断,因为真正作为范围函数的参数传递的是查询的“所有者”实体,在本例中为BrandSubscription
:BrandSubscription.joins(:with_lower_tiers)
我要获取的SQL查询如下,我可以从其中获得用户的所有可用奖励。请注意,我要两次连接
tiers
表,而这正是我遇到麻烦的地方:SELECT DISTINCT rewards.*
FROM tiers
INNER JOIN brand_subscriptions ON tiers.id = brand_subscriptions.tier_id
INNER JOIN tiers tiers_reward ON tiers_reward.brand_id = tiers.brand_id
INNER JOIN rewards ON tiers_reward.id = rewards.tier_id
WHERE tiers_reward.order <= tiers.order
AND brand_subscriptions.user_id = 1234
我相信有些Arel可能会有所帮助,但如果能完全依靠ActiveRecord来完成代码,那将是非常干净的事情,我真的很愿意。
参考文献
我使用以下链接来尝试解决此问题:
Join the same table twice with conditions
https://robots.thoughtbot.com/using-arel-to-compose-sql-queries
http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html
https://gist.github.com/mildmojo/3724189
http://jpospisil.com/2014/06/16/the-definitive-guide-to-arel-the-sql-manager-for-ruby.html
ActiveRecord query with alias'd table names
最佳答案
您可以自己构建查询,而不必依赖复杂的has_many-through关联
class Reward < ActiveRecord::Base
belongs_to :tier
end
class Brand < ActiveRecord::Base
has_many :tiers
end
class Tier < ActiveRecord::Base
belongs_to :brand
has_many :rewards
# has_many :lower_tiers, ->(tier){ where('tiers.order < ?', tier.order) }, through: :brand, source: :tiers
def lower_tiers
Tier.where('brand_id = ? AND order < ?', brand_id, order)
end
def achievable_rewards
Rewards.where('tier_id IN (?) OR tier_id = ?', lower_tiers.select(:id), id)
end
end
class BrandSubscription < ActiveRecord::Base
belongs_to :user
belongs_to :tier
def rewards
tier ? tier.achievable_rewards : Reward.none
end
end