acts_as_ordered_tree
昨日、ツリー構造のデータのためのプラグインを見ていたのですが、ツリー全体の一括での取得が不要であれば、acts_as_ordered_treeがシンプルで使いやすそうでした。acts_as_ordered_treeでは、acts_as_treeと同じく親への参照によりツリー構造を管理し、さらに子同士の間の並び順も保持します(acts_as_listと同じ)。
プラグインのインストールをインストールします。
script/plugin install http://ordered-tree.rubyforge.org/svn/acts_as_ordered_tree/
テスト用のモデルとテーブルを作成します。
script/generate model Item name:string
マイグレーションファイルを編集します。
class Items < ActiveRecord::Migration def self.up create_table :items do |t| t.string :name t.integer :parent_id ,:integer ,:null => false ,:default => 0 # 親への参照 t.integer :position ,:integer # ソート用 t.timestamps end add_index(:items, :parent_id) # インデックス end def self.down drop_table :items end end
item.rbに記述を追加します。
class Item < ActiveRecord::Base acts_as_ordered_tree end
いろいろ操作してみます。
require 'test_helper' class ItemTest < ActiveSupport::TestCase # Replace this with your real tests. def test_ordered_tree # ルートの追加 root = Item.create(:name => 'root-1') # 子の追加 root.children.create(:name => 'item-1-1') root.children << Item.create(:name => 'item-1-2') root.children << Item.create(:name => 'item-1-3') # 孫の追加 root.children[0].children.create(:name => 'item-1-1-1') root.children[0].children.create(:name => 'item-1-1-2') # 子を取得する root = Item.find_by_name('root-1') assert_equal 3, root.children.size assert_equal 'item-1-1', root.children[0].name assert_equal 'item-1-2', root.children[1].name assert_equal 'item-1-3', root.children[2].name root.children.each_with_index { |element, idx| element.position = idx } # 子を並び替える(3 <-> 2) root.children[2].move_higher assert_equal 'item-1-1', root.children[0].name assert_equal 'item-1-3', root.children[1].name assert_equal 'item-1-2', root.children[2].name root.children.each_with_index { |element, idx| element.position = idx } # 子を並び替える(1 <-> 2) root.children[0].move_lower assert_equal 'item-1-3', root.children[0].name assert_equal 'item-1-1', root.children[1].name assert_equal 'item-1-2', root.children[2].name root.children.each_with_index { |element, idx| element.position = idx } # 子を並び替える(item-1-3 => 末尾) root.children[0].move_to_bottom assert_equal 'item-1-1', root.children[0].name assert_equal 'item-1-2', root.children[1].name assert_equal 'item-1-3', root.children[2].name root.children.each_with_index { |element, idx| element.position = idx } # 孫を取得する subitems = root.children[0].children assert_equal 2, subitems.size assert_equal 'item-1-1-1', subitems[0].name assert_equal 'item-1-1-2', subitems[1].name subitems.each_with_index { |element, idx| element.position = idx } # 先祖を取得する ancestors = subitems[0].ancestors assert_equal 2, subitems.size assert_equal 'item-1-1', ancestors[0].name assert_equal 'root-1', ancestors[1].name # item-1-1-1を一つ上に移動 subitems[0].shift_to(root) assert_equal 4, root.children.size assert_equal 'item-1-1', root.children[0].name assert_equal 'item-1-2', root.children[1].name assert_equal 'item-1-3', root.children[2].name assert_equal 'item-1-1-1', root.children[3].name # 移動していること end end
他にもツリーを操作するメソッドはいろいろあります。APIを参照してみてください。