天天看點

将舊有的Acts_As_Tree轉成Acts_As_Threaded

轉換的重點其實隻有一個,重點在于lft和rgt兩個字段的初始化,否則其他字段的初始

化對會寫程式的人應該難不倒才是,轉換的相異點來自于新增四個字段:root_id(根ID)

、lft(左值)、rgt(右值)、depth(深)

改成這樣好處多多,可以使用單一query取得"所有"子代(不限階層),且所有子代count

詳情請參閱Acts_As_Threaded

以下是轉換實做的部份,用于"Cate"這個model

[b][[Migrate]][/b]

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 
           

[b][[cate.rb (Model)]] [/b]

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内一次做完全部的方法 :)