ActiveRecordで多階層カテゴリ


当方、ActiveRecord(Rails)初心者ですがこんな感じかなというのをメモします。

準備

$ rails g model category name:string parent_category_id:integer
$ rake db:migrate
mysql> select * from categories;
+----+---------+--------------------+---------------------+---------------------+
| id | name    | parent_category_id | created_at          | updated_at          |
+----+---------+--------------------+---------------------+---------------------+
|  1 | metal   |               NULL | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
|  2 | melodic |                  1 | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
|  3 | black   |                  1 | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
|  4 | death   |                  1 | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
|  5 | jazz    |               NULL | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
|  6 | swing   |                  5 | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
|  7 | modern  |                  5 | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
|  8 | bebop   |                  5 | 2014-12-27 09:38:14 | 2014-12-27 09:38:14 |
+----+---------+--------------------+---------------------+---------------------+

Model定義

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

確認

$ rails console
> Category.first
=> #<Category id: 1, name: "metal", parent_category_id: nil, created_at: "2014-12-27 08:26:48", updated_at: "2014-12-27 08:26:48">

> Category.first.children
=> #<ActiveRecord::Associations::CollectionProxy [#<Category id: 2, name: "melodic", parent_category_id: 1, created_at: "2014-12-27 08:27:51", updated_at: "2014-12-27 08:27:51">, #<Category id: 3, name: "black", parent_category_id: 1, created_at: "2014-12-27 08:28:27", updated_at: "2014-12-27 08:28:27">, #<Category id: 4, name: "death", parent_category_id: 1, created_at: "2014-12-27 08:29:03", updated_at: "2014-12-27 08:29:03">]>

> Category.last
=> #<Category id: 8, name: "bebop", parent_category_id: 5, created_at: "2014-12-27 08:40:14", updated_at: "2014-12-27 08:40:14">

> Category.last.parent
=> #<Category id: 8, name: "bebop", parent_category_id: 5, created_at: "2014-12-27 08:40:14", updated_at: "2014-12-27 08:40:14">

便利な gem があったりするんでしょうか。

※追記
Active Record Associations
2.10 Self Joins
http://guides.rubyonrails.org/association_basics.html#self-joins

↑を見つけました。こちらの方が t.references で参照しているので良いですね。

「Self Joins」って書いてあったので自己結合してるのかと思ったんですが違いました。

> Category.last
  Category Load (0.2ms)  SELECT  `categories`.* FROM `categories`   ORDER BY `categories`.`id` DESC LIMIT 1

> Category.last.parent
  Category Load (0.3ms)  SELECT  `categories`.* FROM `categories`   ORDER BY `categories`.`id` DESC LIMIT 1
  Category Load (0.3ms)  SELECT  `categories`.* FROM `categories`  WHERE `categories`.`id` = 5 LIMIT 1

※追記2
@jnchito さんにコメントで教えていただいた Ancestry について投稿しました。
ActiveRecordで多階層カテゴリ(Ancestry)