Il y a deux aspects avant que la solution réelle:
- Le diagramme et votre exemple diffèrent dans un aspect très critique. Le diagramme suggère que la relation entre le conteneur et les enfants est un-plusieurs. Cependant, votre exemple montre qu'il est beaucoup-à-plusieurs.
- Il peut être résolu dans les deux cas en utilisant principalement un seul modèle.
Many-to-many
Il peut être résolu en utilisant un grand nombre à plusieurs avec elle-même.
Modèle
class Component < ActiveRecord::Base
# Add as many attributes you need
attr_accessible :name
has_and_belongs_to_many :children,
:class_name => "Component",
:join_table => "children_containers",
:foreign_key => "container_id",
:association_foreign_key => "child_id"
has_and_belongs_to_many :containers,
:class_name => "Component",
:join_table => "children_containers",
:foreign_key => "child_id",
:association_foreign_key => "container_id"
# All Components that do not belong to any container
scope :roots, -> {where("not exists (select * from children_containers where child_id=components.id)")}
# All Components that have no children
scope :leaves, -> {where("not exists (select * from children_containers where container_id=components.id)")}
# Is this Component at root level
def root?
self.containers.empty?
end
# Is this Component at leaf level
def leaf?
self.children.empty?
end
# Notice the recursive call to traverse the Component hierarchy
# Similarly, it can be written to output using nested <ul> and <li>s as well.
def to_s(level=0)
"#{' ' * level}#{name}\n" + children.map {|c| c.to_s(level + 1)}.join
end
end
Migration
class CreateComponents < ActiveRecord::Migration
def change
create_table :components do |t|
t.string :name
t.timestamps
end
create_table :children_containers, :id => false do |t|
t.references :child
t.references :container
end
add_index :children_containers, :child_id
add_index :children_containers, [:container_id, :child_id], :unique => true
end
end
Exemple de code
["R1", "R2", "L1", "L2", "C1", "C2", "C3"].each {|n| Component.create(:name => n)}
[
["R1", "C1"],
["R2", "C2"],
["R1", "C3"],
["R2", "C3"],
["C1", "L1"],
["C2", "L2"],
["C3", "L1"],
["C3", "L2"]
].each {|pair| p,c=pair; Component.find_by_name(p).children << Component.find_by_name(c)}
puts Component.roots.map(&:name).to_s
# ["R1", "R2"]
puts Component.leaves.map(&:name).to_s
# ["L1", "L2"]
puts Component.find_by_name("R1").to_s
# R1
# C1
# L1
# C3
# L1
# L2
One-to-many
Il est beaucoup plus simple i Dans ce cas. Utilisez Ancestry (https://github.com/stefankroes/ancestry) dans le modèle de composant. Il fournira toutes les opérations nécessaires. Alternativement, acts_as_tree peut être utilisé à la place d'Ancestry. Faites-moi savoir si vous avez besoin d'un code d'échantillon pour cela.