counter_cache in single table inheritance
Asked Answered
N

2

7

I am wondering if the counter_cache would work in single table inheritance.

For these models:

class User
  has_many :questions
end

class Question
  belongs_to :user, :counter_cache => true
end

class SimpleQuestion < Question
end
class ComplexQuestion < Question
end

So will the following counters work?

create_table(:users) do |t|
  t.integer :questions_count
  t.integer :simple_questions_count
  t.integer :complex_questions_count
end
  1. All of them work
  2. None of them work
  3. Only questions_count work
  4. Only simple_questions_count and complex_questions_count

Which one? I am guessing the 3rd, but I want 4 more. If it's not 4, how do I make 4 work?

=== UPDATE ===

Here is an example:

id, user_id, question_content, type
1, 3, something, SimpleQuestion
2, 3, something, SimpleQuestion
3, 3, something, ComplexQuestion

So now I want:

user.questions_count # => 3
user.simple_questions_count # => 2
user.complex_questions_count # => 1

My question is, what's the basic behavior of :counter_cache => true and is it possible to apply on single table inheritance?

Noncombatant answered 20/10, 2010 at 16:44 Comment(2)
Are you trying to have simple_questions_count and complex_questions_count return the same count number? Or are those going to count based on some other field on the question table (eg. User submits whether it's simple or complex question)?Indecent
SimpleQuestion and ComplexQuestion are sub-classes of Question, having different type in the same table. Maybe I give an example.Noncombatant
T
6

Looking through the source code where ":counter_cache" is implemented, it does not look like it supports the sort of counting you need. Fortunately, it is easy to roll your own here. Just update Question to track the counts manually, like so:

class Question
  belongs_to :user

  after_create :increment_counts
  before_destroy :decrement_counts

  protected

  def increment_counts
    User.increment_counter :questions_count, user_id
    User.increment_counter "#{type.pluralize.underscore}_count", user_id
  end

  def decrement_counts
    User.decrement_counter :questions_count, user_id
    User.decrement_counter "#{type.pluralize.underscore}_count", user_id
  end
end
Tarshatarshish answered 22/10, 2010 at 20:44 Comment(3)
Shouldn't I use after_create instead of after_save? So you mean the counter_cache would not work for questions_count? How about simple_questions_count and complex_questions_count? They would work on belongs_to :user, :counter_cache => true?Noncombatant
Yes, you're right. It should be afer_create, not after_save. Just a typo on my part. For questions_count, "counter_cache => true" would work, but I figure if you're going to have manual counter callbacks anyway, might as well put the questions_count logic there too. For simple_questions_count and complex_questions_count, Rails currently offers no built-in solution -- that's what the manual callbacks are for.Tarshatarshish
I've updated the code snippet above to fix the after_create typo.Tarshatarshish
H
11

Just faced the same situation and it worked for me as you expected (num 4):

Look, modify your code like this, so that sub-classes would override parents behavior:

class User
  has_many :questions
end

class Question
  belongs_to :user
end

class SimpleQuestion < Question
  belongs_to :user, :counter_cache => true
end
class ComplexQuestion < Question
  belongs_to :user, :counter_cache => true
end

And add complex_questions_count and simple_questions_count columns to your User

Thats it! whenever you create a are question it would increment the proper counter. Tested it on rails 3.2!

Heterozygote answered 19/7, 2013 at 6:23 Comment(0)
T
6

Looking through the source code where ":counter_cache" is implemented, it does not look like it supports the sort of counting you need. Fortunately, it is easy to roll your own here. Just update Question to track the counts manually, like so:

class Question
  belongs_to :user

  after_create :increment_counts
  before_destroy :decrement_counts

  protected

  def increment_counts
    User.increment_counter :questions_count, user_id
    User.increment_counter "#{type.pluralize.underscore}_count", user_id
  end

  def decrement_counts
    User.decrement_counter :questions_count, user_id
    User.decrement_counter "#{type.pluralize.underscore}_count", user_id
  end
end
Tarshatarshish answered 22/10, 2010 at 20:44 Comment(3)
Shouldn't I use after_create instead of after_save? So you mean the counter_cache would not work for questions_count? How about simple_questions_count and complex_questions_count? They would work on belongs_to :user, :counter_cache => true?Noncombatant
Yes, you're right. It should be afer_create, not after_save. Just a typo on my part. For questions_count, "counter_cache => true" would work, but I figure if you're going to have manual counter callbacks anyway, might as well put the questions_count logic there too. For simple_questions_count and complex_questions_count, Rails currently offers no built-in solution -- that's what the manual callbacks are for.Tarshatarshish
I've updated the code snippet above to fix the after_create typo.Tarshatarshish

© 2022 - 2024 — McMap. All rights reserved.