Reputation: 758
A posts model:
class Post < ActiveRecord::Base
has_many :taggings, dependent: :destroy
has_many :tags, through: :taggings
and a tags model:
class Tag < ActiveRecord::Base
has_many :taggings
has_many :posts, through: :taggings
and the associated join table:
class Tagging < ActiveRecord::Base
belongs_to :tag, dependent: :destroy
belongs_to :post
so, the dependent: :destroy in Post makes the db entries in Tagging to be destroyed when doing for instance Post.last.destroy, and the dependent: :destroy in Tagging makes the associated Tag object to be destroyed as well. Fine.
My problem is, 2 posts may share the same Tag, and I would like to destroy that tag -only- if the post being deleted is the last one referencing it. I don't allow duplicate entries in the Tag table. The tags for a post are submitted as a a string with the tags separated by commas, and upon creation of a post I do the following:
def tag_names=(names)
self.tags = names.split(",").map{ |tag| Tag.where(name: tag.squish).first_or_create! }
end
Any idea on how I could achieve this? Thanks!
Upvotes: 0
Views: 741
Reputation: 3347
Are you using a plugin? act_as_taggable or something like that?
If so, take a look to the docs because probably is already implemented.
If not, you can always implement the after_destroy
callback to count the elements associated to a tag and delete the tag in the case that you have "empty" tags.
For instance, in your Post
model:
class Post
before_destroy :clean_up_tags
protected
def clean_up_tags
tags_to_delete = Tagging.where(id: self.tag_ids).group(:tag_id).having("count(distinct taggable_id) = 1").pluck(:id)
Tag.find(tags_to_delete).map(&:destroy)
end
end
This method is assuming that you have a method tag_ids that returns the tags associated to an specific Post and that your taggings model is polymorphic).
As you probably have more than one model with the tags feature, a good approach would be packing this method into a module and include it in all the models, in this way you keep the things DRY.
Upvotes: 1