如何通过查询参数动态添加gorm作用域



我的挑战:根据给定的url参数,我需要动态添加特定的作用域。实施它的最佳方式是什么?

package handler
import(
"net/http"
"gorm.io/gorm"
)
func scopeA(age int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("age > ?", age)
}
}
func scopeB(cc string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("country_code = ?", cc)
}
}
func scopeDefault(db *gorm.DB) *gorm.DB {
return db.Where("active = ?", true)
}
func MyHandler(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query()
user := []model.User{}
// howto add additional scope only if specific url parameter is given?
var age int
var cc string
if values.Has("country_code") {
cc = values.Get("country_code")
}
if values.Has("age") {
age = values.Get("age")
}
if err := db.Scopes(
scopeDefault,
// scopeA(age) only if parameter given,
// scopeB(cc) only if parameter given,
).Find(&user).Error; err != nil {
respondError(w, http.StatusInternalServerError, err.Error())
return
}
respondJSON(w, http.StatusOK, user)
}

我试着看一下gorm Scopes:的实现

// impelementation of gorm Scopes member-function
// https://github.com/go-gorm/gorm/blob/v1.22.5/chainable_api.go#L260
func (db *DB) Scopes(funcs ...func(*DB) *DB) (tx *DB) {
tx = db.getInstance()
tx.Statement.scopes = append(tx.Statement.scopes, funcs...)
return tx
}

但是因为tx.Statement.scopes没有导出,所以似乎不可能简单地附加我的scopeAscopeB

// won't work
tx.Statement.scopes = append(tx.Statement.scopes, scopeA(age))

有人能把我推向正确的方向吗?提前感谢!

这样的东西怎么样?

package main
import (
"fmt"
"net/http"
"reflect"
"github.com/gorilla/schema"
"gorm.io/gorm"
)
type ParamObj struct {
Age         *int    `json:"age" schema:"age" qrstr:"age > ?"`
CountryCode *string `json:"country_code" schema:"country_code" qrstr:"country_code = ?"`
}
func main() {
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
var db *gorm.DB // example open connection
paramObj := new(ParamObj)
err := schema.NewDecoder().Decode(paramObj, r.URL.Query())
if err != nil {
panic(err)
}
fmt.Printf("ParamObj: %+vn", paramObj)
var temp interface{}
db.Scopes(customStructFilter(paramObj)...).Find(&temp)
w.WriteHeader(http.StatusOK)
})
http.ListenAndServe(":8080", nil)
}
func customStructFilter(v interface{}) []func(*gorm.DB) *gorm.DB {
var arr = make([]func(*gorm.DB) *gorm.DB, 0)
vl := reflect.ValueOf(v).Elem()
typ := reflect.TypeOf(v).Elem()
for idx := 0; idx < vl.NumField(); idx++ {
if !vl.Field(idx).IsNil() {
arr = append(arr, func(d *gorm.DB) *gorm.DB {
return d.Where(typ.Field(idx).Tag.Get("qrstr"), vl.Field(idx).Interface())
})
}
}
return arr
}

接受Query参数,使用gorilla/schema将其解码为Object(需要schema标签(,并为customFilter添加qrstr标签。

好吧,如果在scope((和scopeB((中添加一个验证检查,那就足够了。像这样的

// scopeA(r.URL.Query().Get("age"))
func scopeA(age string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
if age == "" {
return db
}
if ageInt, err := strconv.Atoi(age); err == nil {
return db.Where("age > ?", ageInt)
} else {
return db
}
}
}

我想出了一个简单明了的解决方案。只要可能的查询参数的数量不那么多,在我看来这是最合适的。尽管我很喜欢大卫精心设计的解决方案。非常感谢。

package handler
import(
"net/http"
"gorm.io/gorm"
)
func scopeA(age int) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("age > ?", age)
}
}
func scopeB(cc string) func(db *gorm.DB) *gorm.DB {
return func(db *gorm.DB) *gorm.DB {
return db.Where("country_code = ?", cc)
}
}
func scopeDefault(db *gorm.DB) *gorm.DB {
return db.Where("active = ?", true)
}
func MyHandler(w http.ResponseWriter, r *http.Request) {
values := r.URL.Query()
user := []model.User{}
tx := db.Scopes(scopeDefault)
tx, err = buildScopesFromQueryParams(tx, values)
if err != nil {
respondError(w, http.StatusBadRequest, err.Error())
}
if err := tx.Find(&user).Error; err != nil {
respondError(w, http.StatusInternalServerError, err.Error())
return
}
respondJSON(w, http.StatusOK, user)
}

func buildScopesFromQueryParams(tx *gorm.DB, values url.Values) (*gorm.DB, error) {

if values.Has("country_code") {
tx = tx.Scopes(scopeB(strconv.Atoi(values.Get("country_code")))
}

if values.Has("age") {
age, err := strconv.Atoi(values.Get("age")) 

if err != nil {
return tx, err
}

tx = tx.Scopes(scopeA(values.Get("country_code")))
}
return tx, nil
}

最新更新