In my gaming project I have defined couple of models:
class Ticket
belongs_to :user
belongs_to :game
has_and_belongs_to_many :payments, join_table: 'tickets_payments',
association_foreign_key: 'transaction_id',
class_name: 'Transaction'
end
class Transaction
belongs_to :game
has_and_belongs_to_many :tickets, join_table: 'tickets_payments'
end
class Game
has_many :tickets
has_many :paid_tickets, -> { joins(:payments) }, class_name: 'Ticket' # # inner join would return only tickets that have related payment; if the ticket has related payment, it indicates that it was bought/paid
has_many :users, -> { distinct }, through: :paid_tickets
end
The basic idea is that user can see the tickets for the game (I generate 10 random tickets with game_id and user_id but without payment) and when he decides to buy it, I create a payment for the ticket which allows me to filter bought tickets by has_many relation defined on Game model.
Now, my Game class is not related directly to users, so in order to check i.e how many players bought tickets I can do this
Game.find(params[:id]).users.size
which as I believe should logically translate to something like this
Game.find(params[:id]).tickets.joins(:payments).map(&:user).uniq.size
However, the results will vary when there is more than one user who generated the tickets. In the first case I see 2 users (which is wrong, because only one of them actually bought the ticket) and in the second case I correctly receive 1. I was able to fix it some other way, but still it puzzles my why this happens.
From my research I discovered that somehow relation
has_many :users, -> { distinct }, through: :paid_tickets
doesn't see joins(:payments)
defined in paid_tickets has_many relation. I can literally put anything instead of :payments (like joins(:asdasd)
) and it will take all the tickets and still return 2.
Does anyone have a clue why this happens?
I suppose the issue is already fixed and is merged into master, but yet not in stable version (should be fixed in 6.0.3.4). You can consider running self-defined query in this scope to join things for now.