概要
'ancestry'は、階層のあるカテゴリーなどツリー構造を管理するgemです。
今回は以下のような三つの階層に枝分かれしたデータを扱います。
○中学一年
・数量
正負の数 / 文字式 / 資料の活用
・方程式・関数
比例・反比例 / 一次方程式
・図形
平面図形 / 立体図形
○中学二年
・数量
式の計算 / 確率
・方程式・関数
連立方程式 / 一次関数
・図形
平行と合同 / 証明 / 図形の性質
○中学三年
・数量
式の計算 / 平方根 / 標本調査
・方程式・関数
二次方程式 / 二次関数
・図形
三平方の定理 / 図形の性質 / 円周角
準備
1. gemファイルに追加
gem 'ancestry'
2. Categoriesテーブルの作成
$ rails g model category
class CreateCategories < ActiveRecord::Migration[5.2]
def change
create_table :categories do |t|
t.string :name
t.string :ancestry
t.timestamps
end
end
end
ancestryカラムにデータの階層を示す値が入ります
$ rake db:migrate
このようなテーブルになります。
id | name | ancestry |
---|
3. Catagoryモデルにhas_ancestryを追加
class Category < ApplicationRecord
has_ancestry
end
githubによると様々なオプションがあります。
4. データをDBに登録
grade_one = Category.create(name: "中学一年")
one_quantity = grade_one.children.create(name: "数量")
one_function = grade_one.children.create(name: "方程式・関数")
one_shape = grade_one.children.create(name: "図形")
one_quantity.children.create([{name: "正負の数"}, {name: "文字式"}, {name: "資料"}])
one_function.children.create([{name: "比例・反比例"}, {name: "一次方程式"}])
one_shape.children.create([{name: "平面図形"}, {name: "立体図形"}])
grade_two = Category.create(name: "中学二年")
two_quantity = grade_two.children.create(name: "数量")
two_function = grade_two.children.create(name: "方程式・関数")
two_shape = grade_two.children.create(name: "図形")
two_quantity.children.create([{name: "式の計算"}, {name: "確率"}])
two_function.children.create([{name: "連立方程式"}, {name: "一次関数"}])
two_shape.children.create([{name: "平行と合同"}, {name: "証明"}, {name: "図形の性質"}])
grade_three = Category.create(name: "中学三年")
three_quantity = grade_three.children.create(name: "数量")
three_function = grade_three.children.create(name: "方程式・関数")
three_shape = grade_three.children.create(name: "図形")
three_quantity.children.create([{name: "式の計算"}, {name: "平方根"}, {name: "標本調査"}])
three_function.children.create([{name: "二次方程式"}, {name: "二次関数"}])
three_shape.children.create([{name: "三平方の定理"}, {name: "図形の性質"}, {name: "円周角"}])
ancestry(gem)とモデルにhas_ancestryを加えたことにより、childrenメソッドが使えるようになりました。
$ rake db:seed
ancestryがNULLなレコードが一番上の階層、ひとつの数値が書かれているレコードが第二階層、/でわけられたふたつの数値があるものが最下層のデータになっています。
id | name | ancestry |
---|---|---|
1 | 中学一年 | NULL |
2 | 数量 | 1 |
5 | 正負の数 | 1/2 |
親のidとancestryカラムの数値を見ると、それぞれが関連していることがわかります。
値の取得
いよいよ、値の取得方法をおっていきます。
まずはchildrenメソッドで、一つ下の階層の値をすべて取得します。
pry(main)> one_grade = Category.find_by(name: "中学一年")
=> #<Category:0x007fc50033ad98
id: 1,
name: "中学一年",
ancestry: nil,
pry(main)> one_grade.children
=> [#<Category:0x007fc5003a9a40
id: 2,
name: "数量",
ancestry: "1",
#<Category:0x007fc5003a96f8
id: 3,
name: "方程式・関数",
ancestry: "1",
#<Category:0x007fc5003a9540
id: 4,
name: "図形",
ancestry: "1",>]
中学一年.children
→ 数値 / 方程式・関数 / 図形
つぎにindirectsメソッドで一つ階層をまたぎ最下層のデータをすべて取得します。
pry(main)> one_grade.indirects
=> [#<Category:0x007fc504a8f180
id: 5,
name: "正負の数",
ancestry: "1/2",
#<Category:0x007fc504a8f040
id: 6,
name: "文字式",
ancestry: "1/2",
#<Category:0x007fc504a8ef00
id: 7,
name: "資料",
ancestry: "1/2",
#<Category:0x007fc504a8edc0
id: 8,
name: "比例・反比例",
ancestry: "1/3",
#<Category:0x007fc504a8ec80
id: 9,
name: "一次方程式",
ancestry: "1/3",
#<Category:0x007fc504a8eb40
id: 10,
name: "平面図形",
ancestry: "1/4",
#<Category:0x007fc504a8ea00
id: 11,
name: "立体図形",
ancestry: "1/4",
中学一年.indirects
→ 正負の数 / 文字式 / 資料の活用 / 比例・反比例 / 一次方程式 / 平面図形 / 立体図形
上の階層(親)のデータも取得していきましょう
上の階層はparentメソッドです。
pry(main)> solid_figure = Category.find_by(name: "立体図形")
=> #<Category:0x007fc504a67090
id: 11,
name: "立体図形",
ancestry: "1/4",
pry(main)> solid_figure.parent
=> #<Category:0x007fc500142040
id: 4,
name: "図形",
ancestry: "1",
立体図形.parent
→ 図形
ふたつ上の階層の取得はメソッドがないようなのでparentを連鎖させます
pry(main)> solid_figure.parent.parent
=> #<Category:0x007fc500280c68
id: 1,
name: "中学一年",
ancestry: nil,
立体図形.parent.parent
→ 中学一年
まとめ +αのメソッド
Category.root
→ 中学一年 / 中学二年 / 中学三年
中学一年.children
→ 数量 / 方程式・関数 / 図形
中学一年.indirects
→ 正負の数 / 文字式 / 資料の活用 / 比例・反比例 / 一次方程式 / 平面図形 / 立体図形
立体図形.parent
→ 図形
立体図形.parent.parent
→ 中学一年
中学一年.children_ids
→ 2 / 3 / 4
中学一年.indirect_ids
→ 5 / 6 / 7 / 8 / 9 / 10 / 11
他にいくつかのメソッドが用意されています。
詳しくはgithubのREADMEを参照していただきたいです。
なにか間違いがあればご指摘いただけると幸いです!