Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails self-referential has_many through with custom naming of join table

I have some troubles wrapping my head around the following situation..

I am trying to create a tree structure, where I will be able to give custom names to connections between nodes..

So I want to have Node and Relation models. Each

Node 
 has_many :relations

Each

Relation 
 has_many :nodes

Node can be either a parent or a child.. So far everything was easy and there are tons of examples that show how to make a self-referential has_many table... The problem is that I want to be able to give names to relations, so that I can do something like:

relation1 = node1.relations.create(:name => "relation_name", :child => node2)

and in result get something like:

relation1.name == "relation_name"
relation1.parent == node1
relation1.child == node2

All the creations are happening within the model, this activity is not really exposed to user, if that matters. Thanks!

EDIT2: Here is how it works now:

class Node < ActiveRecord::Base
  belongs_to :sentence

  has_one :parent_relation, :foreign_key => "child_id", :class_name => "Relation"
  has_many :child_relations, :foreign_key => "parent_id", :class_name => "Relation"

  has_one :parent, :through => :parent_relation
  has_many :children,  :through => :child_relations, :source => :child

  has_many :relations, :foreign_key => "child_id"
  has_many :relations, :foreign_key => "parent_id"

class Relation < ActiveRecord::Base

  has_many :videos, :as => :videoable, :dependent => :destroy
  has_many :phrases, :through => :videos

  belongs_to :parent, :class_name => "Node"#, :inverse_of => :parent_relation
  belongs_to :child, :class_name => "Node"#, :inverse_of => :child_relation
like image 473
Stpn Avatar asked Mar 29 '12 19:03

Stpn


1 Answers

So what you're talking about is more like a Joins Model than a Self-Reference.

Note: I changed your relation association 'labels' because I was having a hard time with your naming, so you don't have to change your 'labels' that was just for me.

So for your Node class you could do something like this

class Node < ActiveRecord::Base
   has_one  :parent_relation, :foreign_key => "child_id",
                              :class_name => "Relation"
   has_many :child_relations, :foreign_key => "parent_id", 
                              :class_name => "Relation"

   has_one  :parent, :through => :parent_relation
   has_many :children, :through => :child_relations, :source => :child
end

Then for your Relation class you could something like

class Relation < ActiveRecord::Base
  belongs_to :parent, :class_name => "Node", :inverse_of => :parent_relation
  belongs_to :child, :class_name => "Node", :inverse_of => :child_relations
end

The :inverse_of option should let you build let you build a Node based off the parent and children associations from your Node instances, this is just a caveat from the magic with :through relationships. (Documentation for this is at the bottom of the Joins Model section.)

I don't fully understand your association structure, but I think this should model it correctly. Lemme know if there are any problems though.

Side Note: Since Relation is a constant set in the ActiveRecord module you might consider changing it to something like NodeRelationship. I don't think it will interfere with your program, but it definitively caused some trouble for my thought process.

like image 153
Azolo Avatar answered Nov 11 '22 10:11

Azolo