1/11/2007

加強 Active Record 的關連性

很多時候我們會使用 Active Record裡面的條件式關連性,但是有時候會覺得使用的關連性似乎有點太多了、太繁雜了。




像是這樣,有兩個 User 有很多 Email ,但是我們想用 Email 的 status cloumn 來判斷是已讀還是未讀。

基本寫法

通常我們會這樣寫
class User < ActiveRecord::Base
has_many :emails, :dependent => :delete_all
end
我們可以使用來找出那些狀態是未讀,或是已讀的 email
a = User.find(1)
a.emails.find_all_by_status('read')
a.emails.find_all_by_status('unread')
比較好的寫法

但是這樣的壞處在於,假設今天資料庫要重構,我們要將 status 改成 read_status 的時候。我們得將所有的 code 改成
a.emails.find_all_by_read_status('read')
a.emails.find_all_by_read_status('unread')
這樣就變得一點都不 agile 了。所以我們應該這樣寫
class Email < ActiveRecord::Base
def read
@read ||= find(:all, :conditions => "status = 'read'")
end

def unread
@unread ||= find(:all, :conditions => "status = 'unread'")
end
end
如此就可以達成這樣的效果
a.emails.read
a.emails.unread
這樣的寫法。

更好的寫法

上面的好處是他可以簡單的達成不錯的型態,但是這也代表你會在 Email 這個 Model 裡面種入 read / unread 的資訊,當我們使用 Emails 的其他用途,read / unread 的資訊依舊可以使用。但是有些 function 其實是跟著 relationship 來走的,單獨使用並不恰當。這時候我們可以把將 function 值入在 relation function 裡面
class User < ActiveRecord::Base
has_many :emails, :dependent => :delete_all
has_many :read_emails, :class_name => "emails" ,:conditions => "status = 'read'"
has_many :unread_emails , :class_name => "emails" ,:conditions => "status = 'unread'"
end
這樣的作法可以達成下面的效果
a = User.find(1)
a.read_emails
a.unread_emails
另外一種方式

雖然方便,但是也意味著光是一個 Email 我們就建立了三個以上的 relationship,並且假設 table 裡面真的有一個叫做 read_emails 的 table ,那不就衝突了嗎?並且如果這個 function 太過複雜,我們是不是一定得在 :conditions 裡面放入更長的判斷 SQL ,有沒有其他的方式可以將 function 種入 relationship 裡面呢?

當然有,這裡有一篇文章講到一些 Active Record relationship Tips ,我最喜歡他的第二個 tips。

class User < ActiveRecord::Base
has_many :emails, :dependent => :delete_all do
def read(reload=false)
@read = nil if reload
@read ||= find(:all, :conditions => "status = 'read'")
end

def unread(reload=false)
@unread = nil if reload
@unread ||= find(:all, :conditions => "status = 'unread'")
end
end
end
在 relationship 後面,加入這樣的方式,可以做到
a = User.find(1)
a.emails.read
a.emails.unread
如此只需要一個 relationship 就可以做倒類似的事情。你或許會發現這個作法好像比之前的方式還要來得繁雜,但是當 :conditions 過於複雜的時候,這個作法就可以很簡潔的做到這件事情。

2 則留言:

cvu 提到...

谢谢,好方法!优化了我的rails应用。引用的那个blog也是个宝库。

匿名 提到...

seen
欢迎回访我的个人BLOG,
阅读了你的这篇文章,
My Blog:
我的GOOGLE个人空间!
http://tingjing777.blogspot.com/