3

I've added forum_threads_count and forum_posts_count columns to the Forums table. The forum_threads_count works just fine. The forum_posts_count has been reset to "0" instead of showing all of the forum posts that have been created before I added the counter cache columns. The relationships are: Forum has_many :forum_threads, ForumThreads has_many :forum_posts, and Forum has_many :forum_posts, through: :forum_threads.

I later found out that I can't use counter_cache with a has_many through: relationship. So I wrote some private methods to add after_create/after_destroy calls to to increment/decrement the counter. The counter works, it's just that it's still not accounting for all of the forum posts that were created before adding these columns to the Forum table. I feel like it's something wrong with how I wrote the migration. Please help and thank you in advance. I appreciate everyone on this site helping people out.

"...add_counters_to_forums_table.rb"(migration file)

class AddCountersToForumsTableAgain < ActiveRecord::Migration

def self.up
 change_table :forums do |t|
   t.integer :forum_threads_count, :forum_posts_count, default: 0
 end

  Forum.reset_column_information

  Forum.all.pluck(:id).each do |id|
   Forum.reset_counters(id, :forum_posts)
   Forum.reset_counters(id, :forum_threads)
  end
end

 def self.down
  change_table :forums do |t|
   t.remove :forum_threads_count, :forum_posts_count
  end
 end

end

models/forum.rb

class Forum < ActiveRecord::Base

 has_many :forum_threads, -> { order ('updated_at DESC') }, dependent: :destroy 

 accepts_nested_attributes_for :forum_threads
 has_many :forum_posts, through: :forum_threads
 accepts_nested_attributes_for :forum_posts

end

models/forum_thread.rb

class ForumThread < ActiveRecord::Base

 belongs_to :user
 belongs_to :forum, counter_cache: true
 has_many :forum_posts, dependent: :destroy
 accepts_nested_attributes_for :forum_posts

end

models/forum_post.rb

class ForumPost < ActiveRecord::Base

 belongs_to :forum_thread, touch: true
 belongs_to :forum 
 belongs_to :user

  after_create :increment_forum_posts_count
  after_destroy :decrement_forum_posts_count

private

 def increment_forum_posts_count
  Forum.increment_counter( 'forum_posts_count', self.forum_thread.forum.id )
 end

 def decrement_forum_posts_count
  Forum.decrement_counter( 'forum_posts_count', self.forum_thread.forum.id )
 end

end

views/forums/index.html.erb

<%= render 'shared/page_title', title: "Forums" %>
<div class="col-md-10 col-md-offset-1">
<div class="actions">
 <%= link_to "Create New Forum", new_forum_path, class: 'btn btn-primary' %>

    <div class="pull-right">
        <%= form_tag @forum_thread, method: :get do |f| %>
            <%= text_field_tag :q, nil, class: 'form-control', placeholder: 'Search...' %>
        <% end %>
    </div>
</div>

 # LIST FORUMS WITH THREADS AND POSTS COUNTER CACHE
<div class="list-group">
    <% @forums.each do |forum| %>
            <a href="<%= forum_forum_threads_path(forum.id, @forum_threads) %>" class="list-group-item">                
                <h3><%= forum.title %>
                    <div class="pull-right small">                  
                        <%= pluralize forum.forum_threads.size, 'thread' %> |                           
                        <%= pluralize forum.forum_posts.size, 'post' %>                         
                    </div>
                </h3>               
        </a>            
    <% end %>
</div>

BB123
  • 215
  • 3
  • 10

2 Answers2

1

You can add multiple counter_cache columns to your database, and it looks like you're on the right track as far as naming them. To keep them updated, you'll need to modify your ForumThread and ForumPost models to look a bit like this:

ForumThread < ActiveRecord::Base
  ...
  belongs_to :forum, counter_cache: true
  ...
end

There is more information on counter_cache available on the Rails Guides. There's also a RailsCast about counter_caches,

  • Yes, I was familiar with how to set the counter_cache in the model - I was just curious about the migration file. Thank you though for adding that part. I actually followed the RailsCast and it gives an error when trying to run the migration. And getting errors when trying to run migrations is a huge headache for a newbie. That's why I wanted to make sure that the code in the migration file was correct. – BB123 Feb 10 '16 at 03:03
0

Your migration looks fine; you could improve it thus:

#db/migrate/add_counters_to_forums_table_....rb
class AddCountersToForumsTable < ActiveRecord::Migration

  def self.up
    change_table :forums do |t|
      t.integer :forum_threads_count, :forum_posts_count, default: 0
    end

    Forum.all.pluck(:id).each do |id|
      Forum.reset_counters(id, :forum_posts_count)
      Forum.reset_counters(id, :forum_threads_count)
    end
  end

  def self.down
    change_table :forums do |t|
      t.remove :forum_threads_count, :forum_posts_count
    end
  end

end

Good reference: "Adding a Counter Cache to Existing Records"


Because the counter_cache is defined on the association, as long as you have it attached to different associations, you can have as many as you wish:

#app/models/forum.rb
class Forum < ActiveRecord::Base
  has_many :forum_threads
  has_many :forum_posts
end

#app/models/forum_thread.rb
class ForumThread < ActiveRecord::Base
  belongs_to :forum, counter_cache: true
end

#app/models/forum_post.rb
class ForumPost < ActiveRecord::Base
  belongs_to :forum, counter_cache: true
end

Update

I think the issue is that you're using counter_cache on a has_many :through association:

#app/models/forum.rb
class Forum < ActiveRecord::Base
  has_many :forum_threads
  has_many :forum_posts, through: :forum_threads

  delegate :forum_posts_count, to: :forum_threads
end

#app/models/forum_thread.rb
class ForumThread < ActiveRecord::Base
  belongs_to :forum
  has_many :forum_posts
end

#app/models/forum_post.rb
class ForumPost < ActiveRecord::Base
  belongs_to :forum_thread, counter_cache: true
end

Here is a good reference: counter_cache with has_many :through


If you used my code above (for has_many :through), you'll be best going into the Rails console and update the various ForumThreads as follows:

$ rails c
$ forums = Forum.all
$ forums.each do |forum|
$  forum.forum_posts.pluck(:id).each do |id|
$    ForumPost.reset_counters(id, :forum_posts_count)
$  end
$ end

This should reset the counters properly for you.


Update

In regard your question, I still feel you can make the counter_cache work with the has_many :through. I read up on this tutorial (counter_cache with has_many :through), and seems to confirm that you can put the counter_cache in the join model.

Although we aren't doing this per se, I see no reason why it would not work.

In your ForumPost, you really don't need belongs_to :forum -- that should only be applicable for the ForumTopic.

Community
  • 1
  • 1
Richard Peck
  • 76,116
  • 9
  • 93
  • 147
  • So what's the benefit of doing "change_table :forums do..." rather than just "add_column :forums, ...."? – BB123 Feb 10 '16 at 14:54
  • Nothing, it's just cleaner and more succinct – Richard Peck Feb 10 '16 at 15:30
  • thank you for your response. I have a successful migration. **BUT** in the view I have: "`<%= pluralize forum.forum_threads.size, 'thread' %> | <%= pluralize forum.forum_posts.size, 'post' %>`." The forum_threads number has reset and updates normally but the forum_posts number says 0 and won't reset or update. I can't figure out what's happening. – BB123 Feb 10 '16 at 19:16
  • Ok I've got the counters working. Thank you again for the helpful advice. However, the `forum_posts_count` isn't resetting and doesn't show the exact `size` in the view. The counter only shows the forum posts that I've created from when I implemented the counter_cache. I've updated all the files to what I currently have. (the migration file, `forum_threads.rb` and `forum_post.rb` have been modified from the original question). Would it be something in the migration file? – BB123 Feb 11 '16 at 07:16
  • Well I used your code above (with delegate) and the counter_cache wasn't working. I have that code in the migration to reset counters but if you look at the updated question again, I took off the counter_cache for forum_posts and wrote private methods to increment and decrement the forum_posts_count for the forum. – BB123 Feb 11 '16 at 14:57
  • Do you have a repo I could look at? – Richard Peck Feb 11 '16 at 14:57
  • Sure, I'll have to invite you because it's private. What's you Github name? – BB123 Feb 11 '16 at 14:58
  • Thanks! richpeck is the name – Richard Peck Feb 11 '16 at 15:01
  • Thanks, let me check it – Richard Peck Feb 11 '16 at 15:18
  • I don't know what all else you would need besides the files I've already provided. Find anything? – BB123 Feb 12 '16 at 07:10
  • Sorry I had to sleep yesterday, I'm back on it now, though. We should chat about it - do you have a time you're available? – Richard Peck Feb 12 '16 at 10:05
  • It's going to have to be around 1:30pm my time. That might be too late for you though. I have a busy morning. – BB123 Feb 12 '16 at 14:52
  • what time zone are you in? – Richard Peck Feb 12 '16 at 14:56
  • Pacific. California. – BB123 Feb 12 '16 at 16:32
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/103323/discussion-between-bb123-and-richard-peck). – BB123 Feb 12 '16 at 16:39