Rails5.2 and counter_cache on many to many rel.

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 :)