天天看點

acts as list/acts as tree

[color=red]Acts As List[/color]

在子表中使用acts_as_list,便能從父表的“視圖”中得到像子表的行為。父表将能夠

周遊子表,在清單内移動子表,或從清單内移除子表。

通過給每個子表一個位置數來實作清單。這意味着子表必須有個列來記錄此位置。如果

我們稱它為列position,Rails 會自動使用它。如果不這麼稱呼它,我們需要告訴Rails 它

的名字。對于我們的例子,跟随父表之後我們将建立一個新的子表(叫children)。

create table parents (

id int not null auto_increment,

primary key (id)

);

create table children (

id int not null auto_increment,

parent_id int not null,

name varchar(20),

position int,

constraint fk_parent foreign key (parent_id) references parents(id),

primary key (id)

);

接着,我們将建立“模型”類。注意在Parent 類中,我們基于目前的position 列的值

來确定我們children 的位置。這確定從資料庫内獲得的數組在正确的定單清單内。

class Parent < ActiveRecord::Base

has_many :children, :order => :position

end

class Child < ActiveRecord::Base

belongs_to :parent

acts_as_list :scope => :parent_id

end

在Child 類中,我們用傳統的belongs_to 聲明來建立與父表的連接配接。我們也有個

acts_as_list 聲明。我們用一個:scope 選項來限制它,指定清單的每個父表。沒有這範圍操

作符,則對于children 表内的所有條目則隻有一個全局的清單。

現在我們設一些測試用資料:我們為一個特定的父項建立四個子項,稱為One,Two,Three

和Four。

parent = Parent.new

%w{ One Two Three Four}.each do |name|

parent.children.create(:name => name)

end

parent.save

我們要寫個簡單的方法來檢查清單的内容。

def display_children(parent)

puts parent.children.map {|child| child.name }.join(", ")

end

在完成我們清單的測試後。注釋顯示了由display_children()産生的輸出。

display_children(parent) #=> One, Two, Three, Four

puts parent.children[0].first? #=> true

two = parent.children[1]

puts two.lower_item.name #=> Three

puts two.higher_item.name #=> One

parent.children[0].move_lower

parent.reload

display_children(parent) #=> Two, One, Three, Four

parent.children[2].move_to_top

parent.reload

display_children(parent) #=> Three, Two, One, Four

parent.children[2].destroy

parent.reload

display_children(parent) #=> Three, Two, Four

注意我們是如何在父項中調用reload()的。各種move_method 更新資料庫内子項,但因

為它們直接操作子項,父項将不會立即知道這一更改。

list 庫使用術語lower 和higher 來引用元素的相對位置。Higher 意味着離清單前部近,

lower 離尾部近。頂部也與前部是一個意思,底部與尾部是一個意思。方法move_higher( ),

move_lower( ), move_to_bottom( ), 和move_to_top( ) 在list 内循環移動一個特定項

目,并自動調整與其它元素的位置。

higher_item( ) 和lower_item( ) 傳回相對于目前元素的下一個和前一個元素的位

置, first?( ) 和last?( ) 在list 内的目前元素位于前部或尾部時傳回true 。

新近建立的子項被自動地添加到清單的尾部。當一個子行被删除時,list 内的子項會被

移除并用gap 填充。

[color=red]Acts As Tree[/color]

“活動記錄”提供對組織一個表内的行到一個層次,或樹,結構中的支援。這對建立具

有子項的項目很有用處,并且這些子項還可以有它自己的子項。分類目錄清單通常有這種結

構,如對許可證,目錄清單等等的描述。

這種類似樹的結構可通過向表中添加一個單獨的列(預設稱為parent_id)來做到。這個

列是個引用自身表的外鍵,連結子行到它們的父行。圖15.1 示範了它。

要顯示出樹是如何工作的,讓我們建立個簡單的分類表,每個頂層分類有子分類,每個

子分類可以添加子分類層。注意外鍵指回到自身表中。

create table categories (

id int not null auto_increment,

name varchar(100) not null,

parent_id int,

constraint fk_category foreign key (parent_id) references

categories(id),

primary key (id)

);

相應的“模型”使用帶有種類名字acts_as_tree 的方法來指出這種關系。:order 參數

意味着當我們查找特定節點的子項時,我們會看到它們按它們名字列被重新排列。

class Category < ActiveRecord::Base

acts_as_tree :order => "name"

end

通常你得有一些最終使用者功能來建立和管理分類層次。這兒,我們隻使用建立它的代碼。

注意我們如何使用子項屬性來管理任何節點的子項。

root = Category.create(:name => "Books")

fiction = root.children.create(:name => "Fiction")

non_fiction = root.children.create(:name => "Non Fiction")

non_fiction.children.create(:name => "Computers")

non_fiction.children.create(:name => "Science")

non_fiction.children.create(:name => "Art History")

fiction.children.create(:name => "Mystery")

fiction.children.create(:name => "Romance")

fiction.children.create(:name => "Science Fiction")

現在我們做好了所準備工作,我們可以試用樹結構了。我們使用我們用于list 代碼的

display_children()方法。

display_children(root) # Fiction, Non Fiction

sub_category = root.children.first

puts sub_category.children.size #=> 3

display_children(sub_category) #=> Mystery, Romance, Science Fiction

non_fiction = root.children.find(:first, :conditions => "name = 'Non

Fiction'")

display_children(non_fiction) #=> Art History, Computers, Science

puts non_fiction.parent.name #=> Books

我們用于操縱子項的各種方法看起來很熟悉:它們與提供給has_many 的是一樣的。事實

上,如果我們看看acts_as_tree 的實作,我們将看到這做的所有事情都是建構在belongs_to

和has_many 屬性上,每個都指回自身表。就像我們寫的一樣。

class Category < ActiveRecord::Base

belongs_to :parent,

:class_name => "Category"

has_many :children,

:class_name => "Category",

:foreign_key => "parent_id",

:order => "name",

:dependent => true

end

如果你需要優化子項大小的性能,你可以建構一個counter 緩存(就好像你在用

has_many)。添加選項:counter_cache => true 給acts_as_tree 聲明,添加列

children_count 給你的表。

繼續閱讀