在 controller 經常會用到一些資料的查詢條件
但有許多條件是同時會出現在很多地方,或是查詢條件比較複雜,無法一次就看懂在查詢什麼
這時就可以用 Scope 讓程式變得乾淨易讀,而且也可以 串接
來使用。
沒帶參數
1
2
3
def index
@books = Book . order ( create_at : :desc )
end
一般 controller 會這樣子撈出所有的資料,並且按照建立時間排序
這時就可以在 model 寫下
1
2
3
class Book < ActiveRecord :: Base
scope :news_up , -> { order ( created_at : :desc ) }
end
-> {…}是Ruby語法,等同於Proc.new{…}或lambda{…},用來建立一個匿名方法物件
接著 controller 就只要
1
2
3
def index
@books = Book . news_up
end
就變得更加清楚明瞭
帶參數
找尋建立時間小於參數的資料
1
2
3
class Book < ActiveRecord :: Base
scope :recent , -> ( time ) { where ( "created_at < ?" , time ) }
end
1
2
3
4
5
class Book < ActiveRecord :: Base
def self . recent ( time )
where ( "created_at > ? " , time )
end
end
以上兩種方式皆可
接著只要在controller
1
2
3
def index
@books = Book . recent ( Time . now )
end
串接
1
2
3
def index
@books = Book . news_up . recent ( Time . now )
end
1
2
3
4
class Book < ActiveRecord :: Base
scope :recent , -> { where ( published_at : 2 . weeks . ago ) }
scope :recent_red , -> { recent . where ( color : 'red' ) }
end
default
設定所有 scope 的 default 值
1
2
3
4
class Book < ActiveRecord :: Base
default_scope -> { order ( id : :desc ) }
#or default_scope { order(id: :desc) }
end
DRY
where(approved: true)重複了
1
2
3
4
5
6
7
8
9
10
class Comment < ActiveRecord
belongs_to :post
scope :approved , -> { where ( approved : true ) }
end
class Post < ActiveRecord
has_many :comments
scope :with_approved_comments ,
-> { joins ( :comments ) . where ( 'comments.approved = ?' , true ) }
end
Post 改成用 merge 方式,去呼叫另一個 scope
1
2
3
4
5
class Post < ActiveRecord
has_many :comments
scope :with_approved_comments ,
-> { joins ( :comments ) . merge ( Comment . approved ) }
end
Rails3 vs Rails4
1
2
3
4
class User < ActiveRecord :: Base
scope :active , -> { where ( state : 'active' ) }
scope :inactive , -> { where ( state : 'inactive' ) }
end
1
2
3
4
5
6
7
8
9
10
11
#Rails3
User . active . inactive
#SELECT * FROM users WHERE state = 'inactive'
#Rails4
User . active . inactive
#SELECT * FROM posts WHERE state = 'active' AND state = 'inactive'
#Rails4 要跟Rails3 一樣就加上merge
User . active . merge ( User . inactive )
#SELECT * FROM users WHERE state = 'inactive'
官方文件:
Guides
Guides 中文
apidock
參考資料:
Scopes 作用域