多階層カテゴリを扱おうとして、いろいろ苦労したことと、
結局はancestryというgemを使ってうまくいった(多分)という話です。
#完成イメージ
(一部ですが、、、)
・親カテゴリ/子カテゴリ/孫カテゴリがあって、カテゴリ同士に3層の関係性ができている。
・それぞれのカテゴリにアイテムが入っており、カテゴリ分けによってアイテム検索できる。
#①DB設計
当初、アイテムとカテゴリは多対多になるから中間テーブル作って、カテゴリは細分化して入れとけばいいやろ。
と思ってました、、、
↓当初のER図
いざ進めてみると、親子孫の関係性なくね?となり変更。
多階層カテゴリの管理は様々な方法があるようですが、呼び出しがシンプルな
Path Enumeration(経路列挙型)でDBを作成することにしました。
経路をカラムで管理して、経路(Path)基準で表示させましょう、という感じです(あいまいですが)
↓こんな感じ
id | path | item |
---|---|---|
1 | null | レディース |
2 | 1 | トップス |
3 | 1/2 | Tシャツ |
https://kyabatalian.hatenablog.com/entry/2016/12/19/193430
(参考にさせていただいた記事です)
#②導入
https://github.com/stefankroes/ancestry
↑に従えばOKです!
Gemfile
gem 'ancestry'
$ bundle install
$ rails g migration add_ancestry_to_[table] ancestry:string:index
$ rake db:migrate
class Category < ApplicationRecord
has_many :item_categories
has_many :items, through: :item_categories
has_ancestry
end
#カテゴリーモデルにアンセストリーを使うよ、って書いておく
準備完了!
↓(ancestoryを使えば多対多でなく、一対多で良さそうです)
class Item < ApplicationRecord
belongs_to user, foreign_key: 'user_id'
belongs_to :category
#(中略
end
class Category < ApplicationRecord
has_many :items
has_ancestry
end
#③レコード投入!
seeds.rb
lady = Category.create(:name=>"レディース")
lady_tops = lady.children.create(:name=>"トップス")
lady_jacket = lady.children.create(:name=>"ジャケット/アウター")
lady_tops.children.create([{:name=>"Tシャツ/カットソー(半袖/袖なし)"}, {:name=>"Tシャツ/カットソー(七分/長袖)"},{:name=>"その他"}])
lady_jacket.children.create([{:name=>"テーラードジャケット"}, {:name=>"ノーカラージャケット"}, {:name=>"Gジャン/デニムジャケット"},{:name=>"その他"}])
ancestryを入れるとchildrenと記述することで直前の変数の子要素として扱うことができます!
なのでレディース(親)トップス(子)Tシャツ(孫)という関係性をレコード作成の時にGemがやってくれるのです!
$ rake db:seed
1 レディース NULL
2 メンズ NULL
:
13 その他 NULL
14 トップス 1
15 ジャケット/アウター 1
:
19 その他 1
20 Tシャツ/カットソー(半袖/袖なし) 1/14
21 Tシャツ/カットソー(七分/長袖) 1/14
22 シャツ/ブラウス(半袖/袖なし) 1/14
23 シャツ/ブラウス(七分/長袖) 1/14
:
レコード投入完了!
#④Viewで表示
#####コントローラー
def index
@parents = Category.all.order("id ASC").limit(13)
end
#1層目が13個なのでlimit(13)
#####ビュー 親要素
- @parents.each do |parent|
=parent.name
#####子要素
:
- parent.children.each do |child|
=child.name
#####孫要素
:
- child.children.each do |grandchild|
=grandchild.name
#ちなみに、=grandchild.parent.nameとするとgrandchildrenの親要素を取得できます!
#⑤結論
便利すぎます。
呼び出しがシンプルでいいですね。
子から親も呼べますので、いろいろな使い方ができるかも?
まだancestryに関するQiitaの記事は少ないように感じますので、
できればGithubのReadMeを参照にすべきかと考えております。
間違っている点、改善点などありましたらコメントいただけますと幸いです
#参考にさせていただいた記事
https://kyabatalian.hatenablog.com/entry/2016/12/19/193430
https://techracho.bpsinc.jp/hira/2018_03_15/53872
https://qiita.com/NAKANO_Akihito/items/d42a6ceae40933af2352
https://github.com/stefankroes/ancestry