The Context
I have Five models: Reward, BrandSubscription, Brand, Tier and User.
Brand
has manyTiers
.BrandSubscription
belongs to aTier
and toUser
.Reward
belongs to aTier
.Tier
has an attribute namedorder
. If aBrandSubscription
have a tier of a higher order, it will also have all the lower tiers as well.- A
BrandSubscription
can see allRewards
from all itsTiers
, that are in this case theTier
that it belongs to and all its lowerTiers
.
The Problem
My problem is with the last item of the list above. I'm trying to get all rewards of a brand subscription.
The ideal way would be a has_many :rewards
on the BrandSubscription
entity, that would be through: :tier
. On Tier
I could have a has_many :rewards
. The problem with this approach is that the rewards are not restricted to the current tier, but must include all rewards from the lower order
tiers.
To achieve that, I put a scope on the has_many :rewards
of the Tier
model:
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
The problem here, is with this_table
and that_table
. I need to make some kind of a join here, so I could have a comparison between the tables. The one approach I could make work was this:
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
Here I use the tier owner object and get its order. The problem here is that I can't really rely on the tier
argument. The follow query already breaks, because what is really passed as argument to the scope function is the "owner" entity of the query, in this case, BrandSubscription
:
BrandSubscription.joins(:with_lower_tiers)
The SQL query I want to get is the following, where I can get all available rewards from a User. Note that I'm joining the tiers
table twice, and the is precisely where I'm having troubles:
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
I believe some Arel may help but I really would love if I could rely entirely on the ActiveRecord for this, as the code would be much cleaner.
References
I'm using the following links in order to try to solve this problem:
- 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