Scope 是 GORM 提供的查询复用机制。把常用的查询条件封装成函数,在多处复用,保持代码 DRY。
Scope 是一个函数,接收 *gorm.DB 参数,返回 *gorm.DB:
func ActiveUsers(db *gorm.DB) *gorm.DB {
return db.Where("status = ?", "active")
}
通过 Scopes 方法应用:
var users []User
db.Scopes(ActiveUsers).Find(&users)
多个 Scope:
db.Scopes(ActiveUsers, AdultUsers).Find(&users)
状态过滤
func Status(status string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("status = ?", status)
}
}
db.Scopes(Status("active")).Find(&users)
db.Scopes(Status("pending")).Find(&orders)
分页
func Paginate(page, pageSize int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if page < 1 {
page = 1
}
if pageSize < 1 {
pageSize = 10
}
return db.Offset((page - 1) * pageSize).Limit(pageSize)
}
}
db.Scopes(Paginate(1, 10)).Find(&users)
时间范围
func DateRange(field string, start, end time.Time) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if !start.IsZero() {
db = db.Where(field+" >= ?", start)
}
if !end.IsZero() {
db = db.Where(field+" <= ?", end)
}
return db
}
}
db.Scopes(DateRange("created_at", startTime, endTime)).Find(&orders)
软删除过滤
func WithoutDeleted(db *gorm.DB) *gorm.DB {
return db.Where("deleted_at IS NULL")
}
func WithDeleted(db *gorm.DB) *gorm.DB {
return db.Unscoped()
}
排序
func OrderBy(field string, desc bool) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
order := field
if desc {
order += " DESC"
}
return db.Order(order)
}
}
db.Scopes(OrderBy("created_at", true)).Find(&users)
多个 Scope 组合成更复杂的查询:
func ActiveAdults(db *gorm.DB) *gorm.DB {
return db.Where("status = ? AND age >= ?", "active", 18)
}
func RecentCreated(db *gorm.DB) *gorm.DB {
return db.Where("created_at > ?", time.Now().AddDate(0, -1, 0))
}
func OrderedByName(db *gorm.DB) *gorm.DB {
return db.Order("name")
}
// 组合使用
db.Scopes(ActiveAdults, RecentCreated, OrderedByName).Find(&users)
根据条件动态应用 Scope:
func ApplyScopes(db *gorm.DB, scopes ...func(*gorm.DB) *gorm.DB) *gorm.DB {
for _, scope := range scopes {
db = scope(db)
}
return db
}
scopes := []func(*gorm.DB) *gorm.DB{}
if name != "" {
scopes = append(scopes, func(db *gorm.DB) *gorm.DB {
return db.Where("name LIKE ?", "%"+name+"%")
})
}
if status != "" {
scopes = append(scopes, Status(status))
}
ApplyScopes(db, scopes...).Find(&users)
在模型上定义 Scope:
type User struct {
gorm.Model
Name string
Status string
Age int
}
func (User) Active(db *gorm.DB) *gorm.DB {
return db.Where("status = ?", "active")
}
func (User) Adults(db *gorm.DB) *gorm.DB {
return db.Where("age >= ?", 18)
}
// 使用
db.Model(&User{}).Scopes(User.Active, User.Adults).Find(&users)
用户查询
func WithRole(role string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Joins("JOIN user_roles ON user_roles.user_id = users.id").
Joins("JOIN roles ON roles.id = user_roles.role_id").
Where("roles.name = ?", role)
}
}
func SearchByName(name string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if name == "" {
return db
}
return db.Where("name LIKE ?", "%"+name+"%")
}
}
func GetUsers(params UserQueryParams) ([]User, error) {
var users []User
err := db.Model(&User{}).
Scopes(
Status(params.Status),
SearchByName(params.Name),
Paginate(params.Page, params.PageSize),
).
Find(&users).Error
return users, err
}
订单查询
func OrderStatus(status string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if status == "" {
return db
}
return db.Where("status = ?", status)
}
}
func OrderDateRange(start, end time.Time) func(db *gorm.DB) *gorm.DB {
return DateRange("created_at", start, end)
}
func OrderByUser(userID uint) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if userID == 0 {
return db
}
return db.Where("user_id = ?", userID)
}
}
func GetOrders(params OrderQueryParams) ([]Order, error) {
var orders []Order
err := db.Model(&Order{}).
Scopes(
OrderStatus(params.Status),
OrderDateRange(params.StartTime, params.EndTime),
OrderByUser(params.UserID),
Paginate(params.Page, params.PageSize),
).
Order("created_at DESC").
Find(&orders).Error
return orders, err
}
Scope 可以在事务中使用:
err := db.Transaction(func(tx *gorm.DB) error {
var users []User
if err := tx.Scopes(ActiveUsers).Find(&users).Error; err != nil {
return err
}
// 其他操作...
return nil
})
Scope 是查询复用的利器。把常用查询条件封装成 Scope,保持代码简洁可维护。