Scope 复用

Scope 是 GORM 提供的查询复用机制。把常用的查询条件封装成函数,在多处复用,保持代码 DRY。

什么是 Scope

Scope 是一个函数,接收 *gorm.DB 参数,返回 *gorm.DB

func ActiveUsers(db *gorm.DB) *gorm.DB {
    return db.Where("status = ?", "active")
}

使用 Scope

通过 Scopes 方法应用:

var users []User
db.Scopes(ActiveUsers).Find(&users)

多个 Scope:

db.Scopes(ActiveUsers, AdultUsers).Find(&users)

常用 Scope 示例

状态过滤

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

多个 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

根据条件动态应用 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

在模型上定义 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 与事务

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,保持代码简洁可维护。