在 Rails 查詢資料上,除了先前所介紹的 find
, where
的抓取資料的方式之外,
還有一個很特別的方式 - scope! 今天真的來點 scope!
scope 是什麼?
在 Rails 中,scope(作用域)是一種特殊的查詢方法,
scope 查詢方法常見用途是用於按狀態、日期、日期範圍、排序、分組等進行過濾,
可以使用之前介紹過的所有方法,例如 where
, includes
, joins
,
所有作用域會返回一個 ActiveRecord::Relation 或 nil,讓我們能進一步調用其他作用域的方法。
ActiveRecord::Relation 是一個 Class,代表資料庫表的查詢,
可以通過 method chaining (方法鏈)進行進一步的篩選、排序和操作,最終將查詢轉化為 SQL 語句並執行。Active Record::Relation 具有 “惰性加載”(lazy loading)的特性!惰性加載的概念是,當建立一個 Active Record::Relation 時,他不會立即執行與資料庫的實際查詢。相反,他會等到確實需要數據時(例如,當要訪問某結果集合內的資料時),才會生成和執行 SQL 查詢。這個好處是他保留了彈性和可擴展性,能夠動態構建和修改查詢,而不需要立即觸發數據庫查詢。
簡單來說,我們可以在 Model 裡自行定義 scope 查詢方法,以便可以輕鬆地在模型或關聯上調用,而不需要每次都重複相同的查詢邏輯。
使用 scope 時,首先需注意參數問題,每個 Scope 涵蓋兩個參數:
- 在 controller 裡要呼叫的 scope 名稱
:active
- 在查詢方法裡的 lambda (要執行的程式碼)
-> { where(status: 'active') }
1 | class User < ApplicationRecord |
透過定義 :active
的作用域,將查詢所有 status
狀態為 active
的用戶。
在 controller 的首頁中調用 User 模型的 active 作用域:
1 | class UsersController < ApplicationController |
傳遞參數的 scope
scope 可以在定義方法時,傳遞參數 (arguments)!
不帶參數的 scope:
1 | class Book < ApplicationRecord |
1 | class BooksController < ApplicationController |
帶參數的 scope:
1 | class Book < ApplicationRecord |
1 | class BooksController < ApplicationController |
組合技!試試 method chaining
我們來試試組合技!使用方法鏈接(method chaining)來結合這兩個作用域:
1 | class Book < ApplicationRecord |
這樣一來我們就可以用來查找已發布的特定類別的書籍!
1 | class BooksController < ApplicationController |
Default Scope 默認作用域
Default Scope 默認作用域是一種會自動應用於 Model 的所有查詢操作,默認作用域可用於篩選或預設排序 Model 的記錄,以確保在每次查詢時都應用相同的條件。
繼續用 Book
Model 來說明,想要在每次查詢中只顯示已發布的書籍,我們可以這樣做:
1 | class Book < ApplicationRecord |
上述的 scope 無論何時查詢 Book
模型,默認作用域都會自動應用,只顯示已發布的書籍。
我們就不需要額外的操作,每次查詢 Book
模型時都會自動應用這個作用域:
1 | # 查詢所有書籍,只顯示已發布的書籍 |
雖然默認作用域非常方便,但在某些情況下可能需要謹慎使用,因為它會影響模型的所有查詢,包括關聯的查詢。
解除 Default Scope 默認作用域?
要解除 Default Scope 默認作用域,可以使用 unscoped
方法,這個方法將移除默認作用域進行未被作用域影響的查詢。
1 | class Book < ApplicationRecord |
如果您想解除默認作用域,不受 published: true
條件約束的查詢,可以使用 unscoped
方法:
1 | # 解除默認作用域 |
通過使用 unscoped
,可以解除默認作用域的影響,進行不受約束的查詢。但也有可能會導致檢索到未經過濾的記錄,因此應謹慎使用,確保需要解除作用域的原因是合理的。
也就是說,要知道自己是為什麼要用!
Scope 其實就是一種類別方法
Scope(作用域)實際上跟 Class Methods(類別方法)一樣,
Active Record 將 Scope(作用域)轉換為 Class Methods(類別方法),
兩者在本質上是相同的,只是在語法上有些不同。
概念上,Scope(作用域)定義可重用的查詢片段,而這些查詢片段可以像 Class Methods(類別方法)一樣被調用。
Scope(作用域)與 Class Methods(類別方法)差異
Scope(作用域):
1 | class Book < ApplicationRecord |
Class Methods(類別方法):
1 | class Book < ApplicationRecord |
會發現兩者都具有相同的功能且可以在控制器中使用,不過可以透過以下敘述來看看差異的地方:
- Scope(作用域)的好處是具有可讀性,並且可以輕鬆鏈接到其他作用域,當需要接多個查詢條件時,Scope(作用域)是不錯的選擇!
- 經 Scope 出來一定是 ActiveRecord::Relation,但 Class Methods(類別方法)不一定是。(可以回頭看一下 ActiveRecord::Relation 小知識)
- Class Methods(類別方法)使用
def self.method_name
的語法定義。在功能上等效於Scope(作用域),但語法稍微冗長。
Scope 在使用上可以將常用的查詢條件先宣告起來,以備隨時都可以取用,進一步提升可讀性與可維護性,可以實現 DRY(Don’t Repeat Yourself)的原則,在 Active Record 調用中避免重複的代碼,更好的是還可以組合技!
今天就先到這啦~!希望大家都能學習到 scope 的好用之處!我們下篇見!
參考資料:
➫ Active Record Query Interface
➫ Active Record 查詢
➫ PJCHENder - [Rails] Active Record Query(SQL Query & Model 資料查詢)
➫ 資料查找,原來 Ruby on Rails 的 Scope 是這樣用的!_
➫ Ruby on Rails: Scopes_
➫ Active Record scopes vs class methods