背景
我有五个模型:Reward、BrandSubscription、Brand、Tier 和 User。
Brand
有很多Tiers
。BrandSubscription
属于Tier
和User
。Reward
属于一个Tier
。Tier
有一个名为order
的属性。如果BrandSubscription
具有更高级别的层级,那么它也将具有所有较低层级。BrandSubscription
可以看到来自其所有Tiers
的所有Rewards
,在本例中是Tier
它属于及其所有较低的Tiers
。
问题
我的问题是上面列表的最后一项。我正在尝试获得品牌订阅的所有奖励。
理想的方式是 BrandSubscription
实体上的 has_many :rewards
,即 through::tier
。在 Tier
上,我可以有一个 has_many :rewards
。这种方法的问题是奖励不限于当前层,而是必须包括来自较低 order
层的所有奖励。
为了实现这一点,我在 Tier
模型的 has_many :rewards
上设置了一个范围:
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
参数。下面的查询已经中断,因为真正作为参数传递给范围函数的是查询的“所有者”实体,在本例中为 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
关于mysql - 如何动态比较连接表与源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34416722/