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
精彩评论