Laten we beginnen met de relaties een beetje op te knappen:
class Question < ActiveRecord::Base
has_many :options
has_many :answers
has_many :users, through: :answers
end
Er is technisch niets mis met has_many :answers, :through => :options
maar aangezien er een directe relatie is via answers.question_id
we hoeven de options
niet te doorlopen tabel voor de relatie.
De telling weergeven
Als we het gewoon deden:
<td class="optionCell"><%= option.answers.count %></td>
Dit zou een vervelende n+1
. veroorzaken query om het aantal antwoorden voor elke optie op te halen. Dus wat we willen doen is een counter-cache maken
die een telling opslaat in de optietabel.
Laten we beginnen met het maken van een migratie om de kolom toe te voegen:
rails g migration AddAnswerCounterCacheToOptions answers_count:integer
rake db:migrate
Vervolgens vertellen we ActiveRecord om de telling bij te werken wanneer we gekoppelde records maken, dit ziet er een beetje vreemd uit omdat de counter_cache: true
aangifte staat op de belongs_to
kant terwijl de kolom aan de andere kant is, maar dat is gewoon hoe AR werkt.
class Option < ActiveRecord::Base
belongs_to :question
has_many :answers
end
class Answer < ActiveRecord::Base
belongs_to :user
belongs_to :question
belongs_to :option, counter_cache: true
end
Hier zit een klein scheurtje in. Aangezien we misschien al records hebben, moeten we ervoor zorgen dat ze de juiste tellers hebben. Je kunt dit vanaf de console doen, maar op de lange termijn is het een goed idee om maak een rake taak .
Option.find_each { |option| Option.reset_counters(option.id, :answers) }
Dit kan even duren, omdat elke optie moet worden opgehaald en de telling moet worden bijgewerkt.
Nu kunnen we de telling als volgt weergeven:
<% question.options.each do |option| %>
<tr class="backgroundColor1">
<td class="optionCell"><%= option.option_text %></td>
<td class="optionCell"><%= option.answers.size %></td>
</tr>
<% end %>
.size
is slim genoeg om onze counter-cachekolom te gebruiken, maar zal terugvallen op het opvragen van de telling, wat een goede zaak is voor tests.