开发者

Creating a model that has a tree structure

I have categories that are in a tree structure. I am trying to link them together by defining a paren开发者_JS百科t for each one. (I couldn't figure out how to call the property parent so it's just category for now, but it means the parent).

class Category < ActiveRecord::Base

    has_one :category # the parent category

end 

But the relationship ends up the wrong way around.

The getter function is on the child category (correctly) but the category_id is stored on the parent:

parent = Category.create(:name => "parent")
child = Category.create(:name => "child", :category => parent)

parent.id # 1
child.id # 2

child.category_id # nil
parent.category_id # 2

child.category.name # "parent" (!!)

The parent needs to be able to have multiple children so this isn't going to work.


What you're looking for is self joins. Check this section of the Rails guide out: http://guides.rubyonrails.org/association_basics.html#self-joins

class Category < ActiveRecord::Base
  has_many :children, class_name: "Category", foreign_key: "parent_id"
  belongs_to :parent, class_name: "Category"
end

Every Category will belong_to a parent, even your parent categories. You can create a single category parent that your highest level categories all belong to, then you can disregard that information in your application.


You can use acts_as_tree gem to achieve this, find below example and link.

https://github.com/amerine/acts_as_tree/tree/master

class Category < ActiveRecord::Base
  include ActsAsTree

  acts_as_tree order: "name"
end

root      = Category.create("name" => "root")
child1    = root.children.create("name" => "child1")
subchild1 = child1.children.create("name" => "subchild1")

root.parent   # => nil
child1.parent # => root
root.children # => [child1]
root.children.first.children.first # => subchild1


You should take a look at the ancestry gem: https://github.com/stefankroes/ancestry

It provides all the functionality you need and is able to get all descendants, siblings, parents, etc with a single SQL query by using a variant of materialized paths so it'll have better performance than the self-joins and acts_as_tree answers above.


Category should have many categories, and the foreign key of each category should be the parent_id. So, when you do parent.children it lists all the categories which have parent_id=parent.id.

Have you read on Single Table Inheritance?


Full Article - https://blog.francium.tech/best-practices-for-handling-hierarchical-data-structure-in-ruby-on-rails-b5830c5ea64d

A Simple table

Table Emp
id: Integer
name: String
parent_id: Integer

Associations

app/models/emp.rb
class Emp < ApplicationRecord
  has_many :subs, class_name: 'Emp', foreign_key: :parent_id
  belongs_to :superior, class_name: 'Emp', foreign_key: :parent_id
end

Scope Definition

class Emp < ApplicationRecord
  ----
  ----
  scope :roots, -> { where(parent_id: nil) }
end

Fetching data

def tree_data
  output = []
  Emp.roots.each do |emp|
    output << data(emp)
  end
  output.to_json
end
def data(employee)
  subordinates = []
  unless employee.subs.blank?
    employee.subs.each do |emp|
      subordinates << data(emp)
    end
  end
  {name: employee.name, subordinates: subordinates}
end

Eager Loading

def tree_data
  output = []
  Emp.roots.includes(subs: {subs: {subs: subs}}}.each do |emp|
    output << data(emp)
  end
  output.to_json
end
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜