This website is based on the develop branch of the elabs CMS. Everything's not fine, but you can see the changes coming in the next releases.

Counter caches are really useful to keep a track of the amount of related data a given record has. Unfortunately, it's a delicate setup for has_and_belongs_to_many relations

I started to implement experimentslabs.com using Ruby on Rails for learning reasons, and I had a hard time implementing counter caches on has_and_belongs_to_many relations.

First, I tried with callbacks, but the after_destroy callback was not fired, so the counters were only growing up.

Then, I changed the relations to has_many through: :xxx, with the idea of putting the counter_cache declaration in the pivot table. Again, to callback fired on destoy (even with dependent: true). I hope this is not linked to this open issue from 2014 titling "Fails to invoke after_remove callback when deleting associated record"

During my research, I found this great post by jjmars, and I finally came to this solution:

# album.rb
class Album < ApplicationRecord
  has_many :albums_tags
  has_many :tags, through: :albums_tags, dependent: :destroy
end

# tag.rb, contains field "albums_count"
class Tag < ApplicationRecord
  has_many :albums_tags
  has_many :albums, through: :albums_tags
end

# albums_tag.rb
class AlbumsTag < ApplicationContentRecord
  belongs_to :album
  belongs_to :tag

  after_create :increment_counter_cache
  after_destroy :decrement_counter_cache

  private
  def increment_counter_cache
    tag.update(albums_count: (tag.albums_count + 1))
  end

  def decrement_counter_cache
    tag.update(albums_count: (tag.albums_count - 1))
  end
end

Hope this helps :)

Leave a comment

You want to react to this content or ask something to the author? Just leave a comment here!

Note that the comments are not publicly visible, so don't worry if you don't see yours.

All the information you give will only be visible to the author. We don't share anything with anyone.