上下文

我有五个模型:奖励,BrandSubscription,Brand,Tier和User。


Brand有许多Tiers
BrandSubscription属于TierUser
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_tablethat_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

08-06 21:56