mirror of
https://gitee.com/johng/gf
synced 2026-06-06 16:21:40 +08:00
Merge branch 'master' of https://github.com/gogf/gf
This commit is contained in:
@ -320,7 +320,12 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
|
||||
//
|
||||
// See Result.ScanList.
|
||||
func (m *Model) ScanList(structSlicePointer interface{}, bindToAttrName string, relationAttrNameAndFields ...string) (err error) {
|
||||
result, err := m.All()
|
||||
out, err := checkGetSliceElementInfoForScanList(structSlicePointer, bindToAttrName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Filter fields using temporary created struct using reflect.New.
|
||||
result, err := m.Fields(reflect.New(out.BindToAttrType).Interface()).All()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@ -335,7 +340,15 @@ func (m *Model) ScanList(structSlicePointer interface{}, bindToAttrName string,
|
||||
case 1:
|
||||
relationFields = relationAttrNameAndFields[0]
|
||||
}
|
||||
return doScanList(m, result, structSlicePointer, bindToAttrName, relationAttrName, relationFields)
|
||||
return doScanList(doScanListInput{
|
||||
Model: m,
|
||||
Result: result,
|
||||
StructSlicePointer: structSlicePointer,
|
||||
StructSliceValue: out.SliceReflectValue,
|
||||
BindToAttrName: bindToAttrName,
|
||||
RelationAttrName: relationAttrName,
|
||||
RelationFields: relationFields,
|
||||
})
|
||||
}
|
||||
|
||||
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
|
||||
|
||||
@ -85,6 +85,11 @@ import (
|
||||
//
|
||||
// See the example or unit testing cases for clear understanding for this function.
|
||||
func (r Result) ScanList(structSlicePointer interface{}, bindToAttrName string, relationAttrNameAndFields ...string) (err error) {
|
||||
out, err := checkGetSliceElementInfoForScanList(structSlicePointer, bindToAttrName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var (
|
||||
relationAttrName string
|
||||
relationFields string
|
||||
@ -96,27 +101,32 @@ func (r Result) ScanList(structSlicePointer interface{}, bindToAttrName string,
|
||||
case 1:
|
||||
relationFields = relationAttrNameAndFields[0]
|
||||
}
|
||||
return doScanList(nil, r, structSlicePointer, bindToAttrName, relationAttrName, relationFields)
|
||||
return doScanList(doScanListInput{
|
||||
Model: nil,
|
||||
Result: r,
|
||||
StructSlicePointer: structSlicePointer,
|
||||
StructSliceValue: out.SliceReflectValue,
|
||||
BindToAttrName: bindToAttrName,
|
||||
RelationAttrName: relationAttrName,
|
||||
RelationFields: relationFields,
|
||||
})
|
||||
}
|
||||
|
||||
// doScanList converts `result` to struct slice which contains other complex struct attributes recursively.
|
||||
// The parameter `model` is used for recursively scanning purpose, which means, it can scan the attribute struct/structs recursively,
|
||||
// but it needs the Model for database accessing.
|
||||
// Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct.
|
||||
func doScanList(model *Model, result Result, structSlicePointer interface{}, bindToAttrName, relationAttrName, relationFields string) (err error) {
|
||||
if result.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
type checkGetSliceElementInfoForScanListOutput struct {
|
||||
SliceReflectValue reflect.Value
|
||||
BindToAttrType reflect.Type
|
||||
}
|
||||
|
||||
func checkGetSliceElementInfoForScanList(structSlicePointer interface{}, bindToAttrName string) (out *checkGetSliceElementInfoForScanListOutput, err error) {
|
||||
// Necessary checks for parameters.
|
||||
if structSlicePointer == nil {
|
||||
return nil, gerror.NewCode(gcode.CodeInvalidParameter, `structSlicePointer cannot be nil`)
|
||||
}
|
||||
if bindToAttrName == "" {
|
||||
return gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`)
|
||||
return nil, gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`)
|
||||
}
|
||||
|
||||
if relationAttrName == "." {
|
||||
relationAttrName = ""
|
||||
}
|
||||
|
||||
var (
|
||||
reflectType reflect.Type
|
||||
reflectValue = reflect.ValueOf(structSlicePointer)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
@ -125,28 +135,84 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Ptr {
|
||||
return gerror.NewCodef(
|
||||
return nil, gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"structSlicePointer should be type of *[]struct/*[]*struct, but got: %v",
|
||||
reflectKind,
|
||||
"structSlicePointer should be type of *[]struct/*[]*struct, but got: %s",
|
||||
reflect.TypeOf(structSlicePointer).String(),
|
||||
)
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
||||
return gerror.NewCodef(
|
||||
out = &checkGetSliceElementInfoForScanListOutput{
|
||||
SliceReflectValue: reflectValue.Elem(),
|
||||
}
|
||||
// Find the element struct type of the slice.
|
||||
reflectType = reflectValue.Type().Elem().Elem()
|
||||
reflectKind = reflectType.Kind()
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectType = reflectType.Elem()
|
||||
reflectKind = reflectType.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Struct {
|
||||
err = gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
"structSlicePointer should be type of *[]struct/*[]*struct, but got: %v",
|
||||
reflectKind,
|
||||
"structSlicePointer should be type of *[]struct/*[]*struct, but got: %s",
|
||||
reflect.TypeOf(structSlicePointer).String(),
|
||||
)
|
||||
return
|
||||
}
|
||||
// Find the target field by given name.
|
||||
structField, ok := reflectType.FieldByName(bindToAttrName)
|
||||
if !ok {
|
||||
return nil, gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`field "%s" not found in element of "%s"`,
|
||||
bindToAttrName,
|
||||
reflect.TypeOf(structSlicePointer).String(),
|
||||
)
|
||||
}
|
||||
length := len(result)
|
||||
// Find the attribute struct type for ORM fields filtering.
|
||||
reflectType = structField.Type
|
||||
reflectKind = reflectType.Kind()
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectType = reflectType.Elem()
|
||||
reflectKind = reflectType.Kind()
|
||||
}
|
||||
if reflectKind == reflect.Slice || reflectKind == reflect.Array {
|
||||
reflectType = reflectType.Elem()
|
||||
reflectKind = reflectType.Kind()
|
||||
}
|
||||
out.BindToAttrType = reflectType
|
||||
return
|
||||
}
|
||||
|
||||
type doScanListInput struct {
|
||||
Model *Model
|
||||
Result Result
|
||||
StructSlicePointer interface{}
|
||||
StructSliceValue reflect.Value
|
||||
BindToAttrName string
|
||||
RelationAttrName string
|
||||
RelationFields string
|
||||
}
|
||||
|
||||
// doScanList converts `result` to struct slice which contains other complex struct attributes recursively.
|
||||
// The parameter `model` is used for recursively scanning purpose, which means, it can scan the attribute struct/structs recursively,
|
||||
// but it needs the Model for database accessing.
|
||||
// Note that the parameter `structSlicePointer` should be type of *[]struct/*[]*struct.
|
||||
func doScanList(in doScanListInput) (err error) {
|
||||
if in.Result.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
if in.BindToAttrName == "" {
|
||||
return gerror.NewCode(gcode.CodeInvalidParameter, `bindToAttrName should not be empty`)
|
||||
}
|
||||
|
||||
length := len(in.Result)
|
||||
if length == 0 {
|
||||
// The pointed slice is not empty.
|
||||
if reflectValue.Len() > 0 {
|
||||
if in.StructSliceValue.Len() > 0 {
|
||||
// It here checks if it has struct item, which is already initialized.
|
||||
// It then returns error to warn the developer its empty and no conversion.
|
||||
if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
|
||||
if v := in.StructSliceValue.Index(0); v.Kind() != reflect.Ptr {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
}
|
||||
@ -156,10 +222,10 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
var (
|
||||
arrayValue reflect.Value // Like: []*Entity
|
||||
arrayItemType reflect.Type // Like: *Entity
|
||||
reflectType = reflect.TypeOf(structSlicePointer)
|
||||
reflectType = reflect.TypeOf(in.StructSlicePointer)
|
||||
)
|
||||
if reflectValue.Len() > 0 {
|
||||
arrayValue = reflectValue
|
||||
if in.StructSliceValue.Len() > 0 {
|
||||
arrayValue = in.StructSliceValue
|
||||
} else {
|
||||
arrayValue = reflect.MakeSlice(reflectType.Elem(), length, length)
|
||||
}
|
||||
@ -173,17 +239,17 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
relationFromFieldName string // Eg: relationKV: id:uid -> id
|
||||
relationBindToFieldName string // Eg: relationKV: id:uid -> uid
|
||||
)
|
||||
if len(relationFields) > 0 {
|
||||
if len(in.RelationFields) > 0 {
|
||||
// The relation key string of table filed name and attribute name
|
||||
// can be joined with char '=' or ':'.
|
||||
array := gstr.SplitAndTrim(relationFields, "=")
|
||||
array := gstr.SplitAndTrim(in.RelationFields, "=")
|
||||
if len(array) == 1 {
|
||||
// Compatible with old splitting char ':'.
|
||||
array = gstr.SplitAndTrim(relationFields, ":")
|
||||
array = gstr.SplitAndTrim(in.RelationFields, ":")
|
||||
}
|
||||
if len(array) == 1 {
|
||||
// The relation names are the same.
|
||||
array = []string{relationFields, relationFields}
|
||||
array = []string{in.RelationFields, in.RelationFields}
|
||||
}
|
||||
if len(array) == 2 {
|
||||
// Defined table field to relation attribute name.
|
||||
@ -192,12 +258,12 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
// uid:UserId
|
||||
relationFromFieldName = array[0]
|
||||
relationBindToFieldName = array[1]
|
||||
if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationFromFieldName); key == "" {
|
||||
if key, _ := gutil.MapPossibleItemByKey(in.Result[0].Map(), relationFromFieldName); key == "" {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`cannot find possible related table field name "%s" from given relation fields "%s"`,
|
||||
relationFromFieldName,
|
||||
relationFields,
|
||||
in.RelationFields,
|
||||
)
|
||||
} else {
|
||||
relationFromFieldName = key
|
||||
@ -210,13 +276,13 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
}
|
||||
if relationFromFieldName != "" {
|
||||
// Note that the value might be type of slice.
|
||||
relationDataMap = result.MapKeyValue(relationFromFieldName)
|
||||
relationDataMap = in.Result.MapKeyValue(relationFromFieldName)
|
||||
}
|
||||
if len(relationDataMap) == 0 {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`cannot find the relation data map, maybe invalid relation fields given "%v"`,
|
||||
relationFields,
|
||||
in.RelationFields,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -229,19 +295,19 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
bindToAttrField reflect.StructField
|
||||
)
|
||||
if arrayItemType.Kind() == reflect.Ptr {
|
||||
if bindToAttrField, ok = arrayItemType.Elem().FieldByName(bindToAttrName); !ok {
|
||||
if bindToAttrField, ok = arrayItemType.Elem().FieldByName(in.BindToAttrName); !ok {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
|
||||
bindToAttrName,
|
||||
in.BindToAttrName,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if bindToAttrField, ok = arrayItemType.FieldByName(bindToAttrName); !ok {
|
||||
if bindToAttrField, ok = arrayItemType.FieldByName(in.BindToAttrName); !ok {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`invalid parameter bindToAttrName: cannot find attribute with name "%s" from slice element`,
|
||||
bindToAttrName,
|
||||
in.BindToAttrName,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -272,10 +338,10 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
} else {
|
||||
// Like: []Entity
|
||||
}
|
||||
bindToAttrValue = arrayElemValue.FieldByName(bindToAttrName)
|
||||
if relationAttrName != "" {
|
||||
bindToAttrValue = arrayElemValue.FieldByName(in.BindToAttrName)
|
||||
if in.RelationAttrName != "" {
|
||||
// Attribute value of current slice element.
|
||||
relationFromAttrValue = arrayElemValue.FieldByName(relationAttrName)
|
||||
relationFromAttrValue = arrayElemValue.FieldByName(in.RelationAttrName)
|
||||
if relationFromAttrValue.Kind() == reflect.Ptr {
|
||||
relationFromAttrValue = relationFromAttrValue.Elem()
|
||||
}
|
||||
@ -284,10 +350,10 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
relationFromAttrValue = arrayElemValue
|
||||
}
|
||||
if len(relationDataMap) > 0 && !relationFromAttrValue.IsValid() {
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, in.RelationFields)
|
||||
}
|
||||
// Check and find possible bind to attribute name.
|
||||
if relationFields != "" && !relationBindToFieldNameChecked {
|
||||
if in.RelationFields != "" && !relationBindToFieldNameChecked {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToFieldName)
|
||||
if !relationFromAttrField.IsValid() {
|
||||
filedMap, _ := structs.FieldMap(structs.FieldMapInput{
|
||||
@ -299,7 +365,7 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
gcode.CodeInvalidParameter,
|
||||
`cannot find possible related attribute name "%s" from given relation fields "%s"`,
|
||||
relationBindToFieldName,
|
||||
relationFields,
|
||||
in.RelationFields,
|
||||
)
|
||||
} else {
|
||||
relationBindToFieldName = key
|
||||
@ -320,20 +386,20 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
return err
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStructs(bindToAttrValue.Addr()); err != nil {
|
||||
if in.Model != nil {
|
||||
if err = in.Model.doWithScanStructs(bindToAttrValue.Addr()); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Maybe the attribute does not exist yet.
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, in.RelationFields)
|
||||
}
|
||||
} else {
|
||||
return gerror.NewCodef(
|
||||
gcode.CodeInvalidParameter,
|
||||
`relationKey should not be empty as field "%s" is slice`,
|
||||
bindToAttrName,
|
||||
in.BindToAttrName,
|
||||
)
|
||||
}
|
||||
|
||||
@ -363,14 +429,14 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
}
|
||||
} else {
|
||||
// Maybe the attribute does not exist yet.
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, in.RelationFields)
|
||||
}
|
||||
} else {
|
||||
if i >= len(result) {
|
||||
if i >= len(in.Result) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
v := result[i]
|
||||
v := in.Result[i]
|
||||
if v == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
@ -380,8 +446,8 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
}
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStruct(element); err != nil {
|
||||
if in.Model != nil {
|
||||
if err = in.Model.doWithScanStruct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -407,14 +473,14 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
}
|
||||
} else {
|
||||
// Maybe the attribute does not exist yet.
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, relationFields)
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `invalid relation fields specified: "%v"`, in.RelationFields)
|
||||
}
|
||||
} else {
|
||||
if i >= len(result) {
|
||||
if i >= len(in.Result) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
relationDataItem := result[i]
|
||||
relationDataItem := in.Result[i]
|
||||
if relationDataItem == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
@ -424,8 +490,8 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
}
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStruct(bindToAttrValue); err != nil {
|
||||
if in.Model != nil {
|
||||
if err = in.Model.doWithScanStruct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -434,6 +500,6 @@ func doScanList(model *Model, result Result, structSlicePointer interface{}, bin
|
||||
return gerror.NewCodef(gcode.CodeInvalidParameter, `unsupported attribute type: %s`, bindToAttrKind.String())
|
||||
}
|
||||
}
|
||||
reflect.ValueOf(structSlicePointer).Elem().Set(arrayValue)
|
||||
reflect.ValueOf(in.StructSlicePointer).Elem().Set(arrayValue)
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -477,6 +477,133 @@ CREATE TABLE %s (
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_Many_ModelScanList(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user_" + gtime.TimestampMicroStr()
|
||||
tableUserDetail = "user_detail_" + gtime.TimestampMicroStr()
|
||||
tableUserScores = "user_scores_" + gtime.TimestampMicroStr()
|
||||
)
|
||||
if _, err := db.Exec(ctx, fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(ctx, fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(ctx, fmt.Sprintf(`
|
||||
CREATE TABLE %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type EntityUser struct {
|
||||
Uid int `json:"uid"`
|
||||
Name string `json:"name"`
|
||||
}
|
||||
type EntityUserDetail struct {
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
type EntityUserScores struct {
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
type Entity struct {
|
||||
User *EntityUser
|
||||
UserDetail *EntityUserDetail
|
||||
UserScores []*EntityUserScores
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(ctx, tableUser, g.Map{
|
||||
"uid": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// Detail.
|
||||
_, err = db.Insert(ctx, tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
t.AssertNil(err)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(ctx, tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
t.AssertNil(err)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
//db.SetDebug(true)
|
||||
// Result ScanList with struct elements and pointer attributes.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []Entity
|
||||
// User
|
||||
err := db.Model(tableUser).
|
||||
Where("uid", g.Slice{3, 4}).
|
||||
Order("uid asc").
|
||||
ScanList(&users, "User")
|
||||
t.AssertNil(err)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].User, &EntityUser{3, "name_3"})
|
||||
t.Assert(users[1].User, &EntityUser{4, "name_4"})
|
||||
|
||||
// Detail
|
||||
err = db.Model(tableUserDetail).
|
||||
Where("uid", gdb.ListItemValues(users, "User", "Uid")).
|
||||
Order("uid asc").
|
||||
ScanList(&users, "UserDetail", "User", "uid")
|
||||
t.AssertNil(err)
|
||||
t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"})
|
||||
t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"})
|
||||
|
||||
// Scores
|
||||
err = db.Model(tableUserScores).
|
||||
Where("uid", gdb.ListItemValues(users, "User", "Uid")).
|
||||
Order("id asc").
|
||||
ScanList(&users, "UserScores", "User", "uid:Uid")
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_Many_RelationKeyCaseInsensitive(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user_" + gtime.TimestampMicroStr()
|
||||
|
||||
@ -719,10 +719,19 @@ func Test_Model_Count(t *testing.T) {
|
||||
func Test_Model_Select(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
type User struct {
|
||||
Id int
|
||||
Passport string
|
||||
Password string
|
||||
NickName string
|
||||
CreateTime gtime.Time
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
result, err := db.Model(table).All()
|
||||
var users []User
|
||||
err := db.Model(table).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(result), TableSize)
|
||||
t.Assert(len(users), TableSize)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -107,7 +107,7 @@ func (r *Request) doParse(pointer interface{}, requestType int) error {
|
||||
}
|
||||
}
|
||||
// Validation.
|
||||
if err = gvalid.New().Data(pointer, data).Run(r.Context()); err != nil {
|
||||
if err = gvalid.New().Data(pointer).Assoc(data).Run(r.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -126,7 +126,7 @@ func (r *Request) doParse(pointer interface{}, requestType int) error {
|
||||
for i := 0; i < reflectVal2.Len(); i++ {
|
||||
|
||||
if err = gvalid.New().
|
||||
Data(reflectVal2.Index(i), j.Get(gconv.String(i)).Map()).
|
||||
Data(reflectVal2.Index(i)).Assoc(j.Get(gconv.String(i)).Map()).
|
||||
Run(r.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -105,26 +105,33 @@ func (v *Validator) Bail() *Validator {
|
||||
return newValidator
|
||||
}
|
||||
|
||||
// CaseInsensitive sets the mark for Case-Insensitive for those rules that need value comparison.
|
||||
func (v *Validator) CaseInsensitive() *Validator {
|
||||
// Ci sets the mark for Case-Insensitive for those rules that need value comparison.
|
||||
func (v *Validator) Ci() *Validator {
|
||||
newValidator := v.Clone()
|
||||
newValidator.caseInsensitive = true
|
||||
return newValidator
|
||||
}
|
||||
|
||||
// Data is a chaining operation function, which sets validation data for current operation.
|
||||
// The optional parameter `assoc` is usually type of map, which specifies the parameter map used in union validation.
|
||||
// Calling this function with `assoc` also sets `useDataInsteadOfObjectAttributes` true
|
||||
func (v *Validator) Data(data interface{}, assoc ...interface{}) *Validator {
|
||||
func (v *Validator) Data(data interface{}) *Validator {
|
||||
if data == nil {
|
||||
return v
|
||||
}
|
||||
newValidator := v.Clone()
|
||||
newValidator.data = data
|
||||
if len(assoc) > 0 {
|
||||
newValidator.assoc = assoc[0]
|
||||
newValidator.useDataInsteadOfObjectAttributes = true
|
||||
return newValidator
|
||||
}
|
||||
|
||||
// Assoc is a chaining operation function, which sets associated validation data for current operation.
|
||||
// The optional parameter `assoc` is usually type of map, which specifies the parameter map used in union validation.
|
||||
// Calling this function with `assoc` also sets `useDataInsteadOfObjectAttributes` true
|
||||
func (v *Validator) Assoc(assoc interface{}) *Validator {
|
||||
if assoc == nil {
|
||||
return v
|
||||
}
|
||||
newValidator := v.Clone()
|
||||
newValidator.assoc = assoc
|
||||
newValidator.useDataInsteadOfObjectAttributes = true
|
||||
return newValidator
|
||||
}
|
||||
|
||||
|
||||
@ -550,7 +550,7 @@ func (v *Validator) doCheckValueRecursively(ctx context.Context, in doCheckValue
|
||||
validator := v.Clone()
|
||||
validator.rules = nil
|
||||
validator.messages = nil
|
||||
if err := validator.Data(reflect.New(in.Type).Interface(), in.Value).Run(ctx); err != nil {
|
||||
if err := validator.Data(reflect.New(in.Type).Interface()).Assoc(in.Value).Run(ctx); err != nil {
|
||||
// It merges the errors into single error map.
|
||||
for k, m := range err.(*validationError).errors {
|
||||
in.ErrorMaps[k] = m
|
||||
|
||||
@ -199,7 +199,7 @@ func ExampleValidator_Rules() {
|
||||
data := g.Map{
|
||||
"password": "123",
|
||||
}
|
||||
err := g.Validator().Data("", data).
|
||||
err := g.Validator().Data("").Assoc(data).
|
||||
Rules("required-with:password").
|
||||
Messages("请输入确认密码").
|
||||
Run(gctx.New())
|
||||
@ -269,7 +269,7 @@ func ExampleValidator_CheckStruct() {
|
||||
if err := gconv.Scan(data, &user); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
err := g.Validator().Data(user, data).Run(gctx.New())
|
||||
err := g.Validator().Data(user).Assoc(data).Run(gctx.New())
|
||||
if err != nil {
|
||||
fmt.Println(err.Items())
|
||||
}
|
||||
|
||||
@ -414,10 +414,10 @@ func TestValidator_CheckStructWithData(t *testing.T) {
|
||||
Uid: 1,
|
||||
Nickname: "john",
|
||||
}
|
||||
t.Assert(g.Validator().Data(
|
||||
data,
|
||||
g.Map{"uid": 1, "nickname": "john"},
|
||||
).Run(context.TODO()),
|
||||
t.Assert(
|
||||
g.Validator().Data(data).Assoc(
|
||||
g.Map{"uid": 1, "nickname": "john"},
|
||||
).Run(context.TODO()),
|
||||
nil,
|
||||
)
|
||||
})
|
||||
@ -427,7 +427,7 @@ func TestValidator_CheckStructWithData(t *testing.T) {
|
||||
Nickname string `v:"required-with:uid"`
|
||||
}
|
||||
data := UserApiSearch{}
|
||||
t.AssertNE(g.Validator().Data(data, g.Map{}).Run(context.TODO()), nil)
|
||||
t.AssertNE(g.Validator().Data(data).Assoc(g.Map{}).Run(context.TODO()), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type UserApiSearch struct {
|
||||
@ -437,7 +437,7 @@ func TestValidator_CheckStructWithData(t *testing.T) {
|
||||
data := UserApiSearch{
|
||||
Uid: 1,
|
||||
}
|
||||
t.AssertNE(g.Validator().Data(data, g.Map{}).Run(context.TODO()), nil)
|
||||
t.AssertNE(g.Validator().Data(data).Assoc(g.Map{}).Run(context.TODO()), nil)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -451,7 +451,7 @@ func TestValidator_CheckStructWithData(t *testing.T) {
|
||||
StartTime: nil,
|
||||
EndTime: nil,
|
||||
}
|
||||
t.Assert(g.Validator().Data(data, g.Map{}).Run(context.TODO()), nil)
|
||||
t.Assert(g.Validator().Data(data).Assoc(g.Map{}).Run(context.TODO()), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type UserApiSearch struct {
|
||||
@ -464,6 +464,6 @@ func TestValidator_CheckStructWithData(t *testing.T) {
|
||||
StartTime: gtime.Now(),
|
||||
EndTime: nil,
|
||||
}
|
||||
t.AssertNE(g.Validator().Data(data, g.Map{"start_time": gtime.Now()}).Run(context.TODO()), nil)
|
||||
t.AssertNE(g.Validator().Data(data).Assoc(g.Map{"start_time": gtime.Now()}).Run(context.TODO()), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -23,7 +23,7 @@ func Test_CI(t *testing.T) {
|
||||
t.AssertNil(err)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := g.Validator().CaseInsensitive().Rules("in:Id,Name").Data("id").Run(ctx)
|
||||
err := g.Validator().Ci().Rules("in:Id,Name").Data("id").Run(ctx)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
}
|
||||
|
||||
@ -36,7 +36,7 @@ func Test_CustomRule1(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
err := g.Validator().Data("123456").Rules(rule).Messages("custom message").Run(ctx)
|
||||
t.Assert(err.String(), "custom message")
|
||||
err = g.Validator().Data("123456", g.Map{"data": "123456"}).Rules(rule).Messages("custom message").Run(ctx)
|
||||
err = g.Validator().Data("123456").Assoc(g.Map{"data": "123456"}).Rules(rule).Messages("custom message").Run(ctx)
|
||||
t.Assert(err, nil)
|
||||
})
|
||||
// Error with struct validation.
|
||||
@ -176,7 +176,7 @@ func TestValidator_RuleFunc(t *testing.T) {
|
||||
err = g.Validator().
|
||||
Rules(ruleName).
|
||||
Messages("custom message").
|
||||
Data("123456", g.Map{"data": "123456"}).
|
||||
Data("123456").Assoc(g.Map{"data": "123456"}).
|
||||
RuleFunc(ruleName, ruleFunc).
|
||||
Run(ctx)
|
||||
t.AssertNil(err)
|
||||
@ -232,7 +232,7 @@ func TestValidator_RuleFuncMap(t *testing.T) {
|
||||
err = g.Validator().
|
||||
Rules(ruleName).
|
||||
Messages("custom message").
|
||||
Data("123456", g.Map{"data": "123456"}).
|
||||
Data("123456").Assoc(g.Map{"data": "123456"}).
|
||||
RuleFuncMap(map[string]gvalid.RuleFunc{
|
||||
ruleName: ruleFunc,
|
||||
}).Run(ctx)
|
||||
|
||||
@ -59,7 +59,7 @@ func Test_CheckStruct_Recursive_Struct_WithData(t *testing.T) {
|
||||
"Pass2": 200,
|
||||
},
|
||||
}
|
||||
err := g.Validator().Data(user, data).Run(ctx)
|
||||
err := g.Validator().Data(user).Assoc(data).Run(ctx)
|
||||
t.AssertNE(err, nil)
|
||||
t.Assert(err.Maps()["Name"], nil)
|
||||
t.Assert(err.Maps()["Pass1"], g.Map{"same": "The Pass1 value `100` must be the same as field Pass2"})
|
||||
|
||||
@ -44,10 +44,10 @@ func Test_Required(t *testing.T) {
|
||||
if m := g.Validator().Data("").Rules("required").Messages(nil).Run(ctx); m == nil {
|
||||
t.Error(m)
|
||||
}
|
||||
if m := g.Validator().Data("", map[string]interface{}{"id": 1, "age": 19}).Rules("required-if: id,1,age,18").Messages(nil).Run(ctx); m == nil {
|
||||
if m := g.Validator().Data("").Assoc(map[string]interface{}{"id": 1, "age": 19}).Rules("required-if: id,1,age,18").Messages(nil).Run(ctx); m == nil {
|
||||
t.Error("Required校验失败")
|
||||
}
|
||||
if m := g.Validator().Data("", map[string]interface{}{"id": 2, "age": 19}).Rules("required-if: id,1,age,18").Messages(nil).Run(ctx); m != nil {
|
||||
if m := g.Validator().Data("").Assoc(map[string]interface{}{"id": 2, "age": 19}).Rules("required-if: id,1,age,18").Messages(nil).Run(ctx); m != nil {
|
||||
t.Error("Required校验失败")
|
||||
}
|
||||
}
|
||||
@ -55,20 +55,20 @@ func Test_Required(t *testing.T) {
|
||||
func Test_RequiredIf(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
rule := "required-if:id,1,age,18"
|
||||
t.AssertNE(g.Validator().Data("", g.Map{"id": 1}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.Assert(g.Validator().Data("", g.Map{"id": 0}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.AssertNE(g.Validator().Data("", g.Map{"age": 18}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.Assert(g.Validator().Data("", g.Map{"age": 20}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.AssertNE(g.Validator().Data("").Assoc(g.Map{"id": 1}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.Assert(g.Validator().Data("").Assoc(g.Map{"id": 0}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.AssertNE(g.Validator().Data("").Assoc(g.Map{"age": 18}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.Assert(g.Validator().Data("").Assoc(g.Map{"age": 20}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_RequiredUnless(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
rule := "required-unless:id,1,age,18"
|
||||
t.Assert(g.Validator().Data("", g.Map{"id": 1}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.AssertNE(g.Validator().Data("", g.Map{"id": 0}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.Assert(g.Validator().Data("", g.Map{"age": 18}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.AssertNE(g.Validator().Data("", g.Map{"age": 20}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.Assert(g.Validator().Data("").Assoc(g.Map{"id": 1}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.AssertNE(g.Validator().Data("").Assoc(g.Map{"id": 0}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.Assert(g.Validator().Data("").Assoc(g.Map{"age": 18}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
t.AssertNE(g.Validator().Data("").Assoc(g.Map{"age": 20}).Rules(rule).Messages(nil).Run(ctx), nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -86,9 +86,9 @@ func Test_RequiredWith(t *testing.T) {
|
||||
"id": 100,
|
||||
"name": "john",
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.Assert(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
t.AssertNE(err3, nil)
|
||||
@ -106,9 +106,9 @@ func Test_RequiredWith(t *testing.T) {
|
||||
params3 := g.Map{
|
||||
"time": time.Time{},
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.Assert(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
t.Assert(err3, nil)
|
||||
@ -125,9 +125,9 @@ func Test_RequiredWith(t *testing.T) {
|
||||
params3 := g.Map{
|
||||
"time": time.Now(),
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.Assert(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
t.AssertNE(err3, nil)
|
||||
@ -175,9 +175,9 @@ func Test_RequiredWithAll(t *testing.T) {
|
||||
"id": 100,
|
||||
"name": "john",
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.Assert(err1, nil)
|
||||
t.Assert(err2, nil)
|
||||
t.AssertNE(err3, nil)
|
||||
@ -198,9 +198,9 @@ func Test_RequiredWithOut(t *testing.T) {
|
||||
"id": 100,
|
||||
"name": "john",
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.AssertNE(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
t.Assert(err3, nil)
|
||||
@ -221,9 +221,9 @@ func Test_RequiredWithOutAll(t *testing.T) {
|
||||
"id": 100,
|
||||
"name": "john",
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.AssertNE(err1, nil)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(err3, nil)
|
||||
@ -914,9 +914,9 @@ func Test_Same(t *testing.T) {
|
||||
"id": 100,
|
||||
"name": "john",
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.AssertNE(err1, nil)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(err3, nil)
|
||||
@ -937,9 +937,9 @@ func Test_Different(t *testing.T) {
|
||||
"id": 100,
|
||||
"name": "john",
|
||||
}
|
||||
err1 := g.Validator().Data(val1, params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1, params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1, params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
err1 := g.Validator().Data(val1).Assoc(params1).Rules(rule).Messages(nil).Run(ctx)
|
||||
err2 := g.Validator().Data(val1).Assoc(params2).Rules(rule).Messages(nil).Run(ctx)
|
||||
err3 := g.Validator().Data(val1).Assoc(params3).Rules(rule).Messages(nil).Run(ctx)
|
||||
t.Assert(err1, nil)
|
||||
t.AssertNE(err2, nil)
|
||||
t.AssertNE(err3, nil)
|
||||
|
||||
Reference in New Issue
Block a user