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