Further to swapnilabnave's great answer, I'll explain what you're looking for:
I would recommend breaking up your problem into more modular issues
Your question is whether you can use has_many :through
for the relation. The answer, as explained, is "yes", but that won't solve the problem in its entirety
Hierarchy Of Tags
As mentioned, your tags will have "parents" and other items
Although I don't have huge experience with this in Rails, in CakePHP, this will be known as a tree structure. CakePHP handles this by having 3 columns - parent_id
, left
, right
. These basically allow the app to specifically show the various items in your tree based on which numbers are used in those 3 columns
In your case, I'd recommend putting a parent_id
column in the tags
database, which you'll be able to assign a super tag to if required (it seems you only have one super-tag per tag?). You could handle this in your model using the self-referencing association technique that swapnilabnave posted to give the ability to call the super_tag
like this:
@tag.super_tag
And important note would be that this would only work if you could only have one super_tag
per tag. If you wanted an unlimited number of super tags per tag, you'd do this:
class Tag < ActiveRecord::Base
has_many :sub_tags, class_name: "Tag", :foreign_key => "super_tag_id"
has_many :tag_super_tags, class_name: "TagSuperTag", :foreign_key => "super_tag_id"
has_many :super_tags, :through => :tag_super_tags
end
Class TagSuperTag
belongs_to :tag
belongs_to :super_tag, :class => "Tag"
end
Although I'm not sure if the join model code will work, it will hopefully show you the idea here. This would create a self-referencing join model called TagSuperTag, and allow you to have a table like this:
tag_super_tags
id | tag_id | super_tag_id | created_at | updated_at
tags (no need for super_tag_id)
id | user_id | name | etc
This will allow you to add as many super tags as you wish to each tag
User Has Many Tags
Because each user will have many tags, you can just use the standard has_many
association, like this:
class User < ActiveRecord::Base
has_many :tags
end
class Tag < ActiveRecord::Base
belongs_to :user
end
This means that each tag will have to have a user_id
column, to act as a foreign_key
Tags Are Universal
Making tags universal (not just for item
) will require a polymorphic association
, and what seems to be a join model
As swapnilabnave has described, this join model can be accessed by any model which wants to use it to store tags, thus allowing you to associate any model with it. Here's how you could do this:
class TaggedItem < ActiveRecord::Base
belongs_to :taggable, polymorphic: true
belongs_to :tag
end
tagged_items
id | taggable_type | taggable_id | tag_id | created_at | updated_at
This will allow you to reference this model from any other, like this:
class Post < ActiveRecord::Base
has_many :tagged_items, :class_name => "TaggedItem", :as => :taggable, :dependent => :destroy
has_many :tags, :through => :tagged_items
end
class Email < ActiveRecord::Base
has_many :tagged_items, :class_name => "TaggedItem", :as => :taggable, :dependent => :destroy
has_many :tags, :through => :tagged_items
end
And the Tag
model should has_many :tagged_items
, too:
class Tag < ActiveRecord::Base
has_many :tagged_items, :class_name => "TaggedItem", :foreign_key => "tag_id", :dependent => :destroy
end
Hope this helps