diff --git a/database/gdb/gdb_model_select.go b/database/gdb/gdb_model_select.go index 9e4006c61..3bd195c61 100644 --- a/database/gdb/gdb_model_select.go +++ b/database/gdb/gdb_model_select.go @@ -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. diff --git a/database/gdb/gdb_type_result_scanlist.go b/database/gdb/gdb_type_result_scanlist.go index 95d0712ca..238f30bc7 100644 --- a/database/gdb/gdb_type_result_scanlist.go +++ b/database/gdb/gdb_type_result_scanlist.go @@ -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 } diff --git a/database/gdb/gdb_z_mysql_feature_scanlist_test.go b/database/gdb/gdb_z_mysql_feature_scanlist_test.go index a167e0a6d..0388e2fa9 100644 --- a/database/gdb/gdb_z_mysql_feature_scanlist_test.go +++ b/database/gdb/gdb_z_mysql_feature_scanlist_test.go @@ -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() diff --git a/database/gdb/gdb_z_mysql_model_test.go b/database/gdb/gdb_z_mysql_model_test.go index 783971780..a8f0db125 100644 --- a/database/gdb/gdb_z_mysql_model_test.go +++ b/database/gdb/gdb_z_mysql_model_test.go @@ -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) }) } diff --git a/net/ghttp/ghttp_request_param.go b/net/ghttp/ghttp_request_param.go index 9d81c2196..c6c146972 100644 --- a/net/ghttp/ghttp_request_param.go +++ b/net/ghttp/ghttp_request_param.go @@ -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 } diff --git a/util/gvalid/gvalid_validator.go b/util/gvalid/gvalid_validator.go index 177d06335..80ac39d9a 100644 --- a/util/gvalid/gvalid_validator.go +++ b/util/gvalid/gvalid_validator.go @@ -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 } diff --git a/util/gvalid/gvalid_validator_check_value.go b/util/gvalid/gvalid_validator_check_value.go index 4bdaad1e5..4635d5068 100644 --- a/util/gvalid/gvalid_validator_check_value.go +++ b/util/gvalid/gvalid_validator_check_value.go @@ -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 diff --git a/util/gvalid/gvalid_z_example_test.go b/util/gvalid/gvalid_z_example_test.go index 1699318f6..1c52326b6 100644 --- a/util/gvalid/gvalid_z_example_test.go +++ b/util/gvalid/gvalid_z_example_test.go @@ -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()) } diff --git a/util/gvalid/gvalid_z_unit_feature_checkstruct_test.go b/util/gvalid/gvalid_z_unit_feature_checkstruct_test.go index d3c51d71c..82b35f1ab 100755 --- a/util/gvalid/gvalid_z_unit_feature_checkstruct_test.go +++ b/util/gvalid/gvalid_z_unit_feature_checkstruct_test.go @@ -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) }) } diff --git a/util/gvalid/gvalid_z_unit_feature_ci_test.go b/util/gvalid/gvalid_z_unit_feature_ci_test.go index 969603ce0..514fbb5e0 100644 --- a/util/gvalid/gvalid_z_unit_feature_ci_test.go +++ b/util/gvalid/gvalid_z_unit_feature_ci_test.go @@ -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) }) } diff --git a/util/gvalid/gvalid_z_unit_feature_custom_rule_test.go b/util/gvalid/gvalid_z_unit_feature_custom_rule_test.go index af9110fa6..717261404 100644 --- a/util/gvalid/gvalid_z_unit_feature_custom_rule_test.go +++ b/util/gvalid/gvalid_z_unit_feature_custom_rule_test.go @@ -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) diff --git a/util/gvalid/gvalid_z_unit_feature_recursive_test.go b/util/gvalid/gvalid_z_unit_feature_recursive_test.go index dfa098254..e71d3cf1b 100755 --- a/util/gvalid/gvalid_z_unit_feature_recursive_test.go +++ b/util/gvalid/gvalid_z_unit_feature_recursive_test.go @@ -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"}) diff --git a/util/gvalid/gvalid_z_unit_feature_rule_test.go b/util/gvalid/gvalid_z_unit_feature_rule_test.go index aea6e018b..9aef7a934 100755 --- a/util/gvalid/gvalid_z_unit_feature_rule_test.go +++ b/util/gvalid/gvalid_z_unit_feature_rule_test.go @@ -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)