转换的重点其实只有一个,重点在于lft和rgt两个字段的初始化,否则其他字段的初始
化对会写程序的人应该难不倒才是,转换的相异点来自于新增四个字段:root_id(根ID)
、lft(左值)、rgt(右值)、depth(深)
改成这样好处多多,可以使用单一query取得"所有"子代(不限阶层),且所有子代count
详情请参阅Acts_As_Threaded
以下是转换实做的部份,用于"Cate"这个model
[[Migrate]]
class FixCate < ActiveRecord::Migration
def self.up
puts "fixing Cate"
Cate.reset_column_information
Cate.find(:all).each do |c|
if c.parent_id == nil
c.parent_id = 0
c.update
end
end
puts "fixed Cate"
change_column "cates" , "parent_id", :integer, :default => 0, :null => false
add_column "cates" , "root_id", :integer, :default => 0, :null => false
add_column "cates" , "lft", :integer
add_column "cates" , "rgt", :integer
add_column "cates" , "depth", :integer, :default => 0, :null => false
puts "fixing Cate"
Cate.reset_column_information
Cate.find(:all).each do |c|
if c.parent_id == 0
c.root_id = c.id
c.update
end
end
puts "fixed Cate"
end
def self.down
change_column "cates" , "parent_id", :integer
remove_column "cates" , "root_id"
remove_column "cates" , "lft"
remove_column "cates" , "rgt"
remove_column "cates" , "depth"
puts "fixing Cate"
Cate.reset_column_information
Cate.find(:all).each do |c|
if c.parent_id == 0
c.parent_id = nil
c.update
end
end
puts "fixed Cate"
end
end
[[cate.rb (Model)]]
class Cate < ActiveRecord::Base
acts_as_threaded
def parent
if self.parent_id == 0
return nil
else
return Cate.find(self.parent_id)
end
end
def children
#direct_children与all_children选一个使用
return self.direct_children
end
end
[[cates_controller.rb (Controller)]]
class CatesController < ApplicationController
def init
Cate.find(:all).each do |c|
c.before_create
c.after_create
end
end
end
简单的来说,就是在migrate时做基本的转换与初始化,将parent_id = null转为parent_id = 0,然后将parent_id = 0的实体(root)的root_id改为自己(self.id),这样就完成基本的两个字段的初始化。
Model内新增两个method来复写掉继承的mothod,Acts_As_Threaded预设没有parent与children这两个method,且如果之前用Acts_As_Tree实做的话,通常root实体的parent的值通常为null,而修正后会变为0,所以需要另外复写。
因为migrate没有支持完整的Rails与AR与plugin,所以最后一步的初始化要留到全部建立好后,在Controller内执行才行,上面的Controller内写的init就是初始化的方式,执行一次即可。
如果详细看Acts_As_Threaded内的语法就会得知lft与rgt的migeate为何没加入":default => 0, :null => false"这段语法与为何要那样的初始化,不过还是希望有神人可以想出在migrate内一次做完全部的方法