[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 給你的表。