加強 Active Record 的關連性
很多時候我們會使用 Active Record裡面的條件式關連性,但是有時候會覺得使用的關連性似乎有點太多了、太繁雜了。
像是這樣,有兩個 User 有很多 Email ,但是我們想用 Email 的 status cloumn 來判斷是已讀還是未讀。
基本寫法
通常我們會這樣寫
但是這樣的壞處在於,假設今天資料庫要重構,我們要將 status 改成 read_status 的時候。我們得將所有的 code 改成
更好的寫法
上面的好處是他可以簡單的達成不錯的型態,但是這也代表你會在 Email 這個 Model 裡面種入 read / unread 的資訊,當我們使用 Emails 的其他用途,read / unread 的資訊依舊可以使用。但是有些 function 其實是跟著 relationship 來走的,單獨使用並不恰當。這時候我們可以把將 function 值入在 relation function 裡面
雖然方便,但是也意味著光是一個 Email 我們就建立了三個以上的 relationship,並且假設 table 裡面真的有一個叫做 read_emails 的 table ,那不就衝突了嗎?並且如果這個 function 太過複雜,我們是不是一定得在 :conditions 裡面放入更長的判斷 SQL ,有沒有其他的方式可以將 function 種入 relationship 裡面呢?
當然有,這裡有一篇文章講到一些 Active Record relationship Tips ,我最喜歡他的第二個 tips。
像是這樣,有兩個 User 有很多 Email ,但是我們想用 Email 的 status cloumn 來判斷是已讀還是未讀。
基本寫法
通常我們會這樣寫
我們可以使用來找出那些狀態是未讀,或是已讀的 emailclass User < ActiveRecord::Base
has_many :emails, :dependent => :delete_all
end
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')這樣就變得一點都不 agile 了。所以我們應該這樣寫
a.emails.find_all_by_read_status('unread')
如此就可以達成這樣的效果class Email < ActiveRecord::Baseend
def read
@read ||= find(:all, :conditions => "status = 'read'")
end
def unread
@unread ||= find(:all, :conditions => "status = 'unread'")
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。
在 relationship 後面,加入這樣的方式,可以做到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 就可以做倒類似的事情。你或許會發現這個作法好像比之前的方式還要來得繁雜,但是當 :conditions 過於複雜的時候,這個作法就可以很簡潔的做到這件事情。a = User.find(1)
a.emails.read
a.emails.unread
2 則留言:
谢谢,好方法!优化了我的rails应用。引用的那个blog也是个宝库。
seen
欢迎回访我的个人BLOG,
阅读了你的这篇文章,
My Blog:
我的GOOGLE个人空间!
http://tingjing777.blogspot.com/
張貼留言