diff --git a/internal/empty/empty.go b/internal/empty/empty.go index 234378aca..1d42441ff 100644 --- a/internal/empty/empty.go +++ b/internal/empty/empty.go @@ -9,6 +9,7 @@ package empty import ( "reflect" + "time" ) // apiString is used for type assert api for String(). @@ -26,6 +27,11 @@ type apiMapStrAny interface { MapStrAny() map[string]interface{} } +type apiTime interface { + Date() (year int, month time.Month, day int) + IsZero() bool +} + // IsEmpty checks whether given `value` empty. // It returns true if `value` is in: 0, nil, false, "", len(slice/map/chan) == 0, // or else it returns false. @@ -80,6 +86,12 @@ func IsEmpty(value interface{}) bool { return len(value) == 0 default: // Common interfaces checks. + if f, ok := value.(apiTime); ok { + if f == nil { + return true + } + return f.IsZero() + } if f, ok := value.(apiString); ok { if f == nil { return true diff --git a/os/gtime/gtime_time.go b/os/gtime/gtime_time.go index 997cf43ad..871013ed4 100644 --- a/os/gtime/gtime_time.go +++ b/os/gtime/gtime_time.go @@ -220,6 +220,15 @@ func (t *Time) String() string { return t.Format("Y-m-d H:i:s") } +// IsZero reports whether t represents the zero time instant, +// January 1, year 1, 00:00:00 UTC. +func (t *Time) IsZero() bool { + if t == nil { + return true + } + return t.Time.IsZero() +} + // Clone returns a new Time object which is a clone of current time object. func (t *Time) Clone() *Time { return New(t.Time) diff --git a/util/gvalid/gvalid_validator_check.go b/util/gvalid/gvalid_validator_check.go index a349f3486..831930df1 100644 --- a/util/gvalid/gvalid_validator_check.go +++ b/util/gvalid/gvalid_validator_check.go @@ -47,13 +47,11 @@ func (v *Validator) doCheck(key string, value interface{}, rules string, message // It converts value to string and then does the validation. var ( // Do not trim it as the space is also part of the value. - data = make(map[string]string) + data = make(map[string]interface{}) errorMsgArray = make(map[string]string) ) if len(params) > 0 { - for k, v := range gconv.Map(params[0]) { - data[k] = gconv.String(v) - } + data = gconv.Map(params[0]) } // Custom error messages handling. var ( @@ -150,7 +148,7 @@ func (v *Validator) doCheckBuildInRules( ruleKey string, rulePattern string, ruleItems []string, - dataMap map[string]string, + dataMap map[string]interface{}, customMsgMap map[string]string, ) (match bool, err error) { valueStr := gconv.String(value) @@ -235,7 +233,7 @@ func (v *Validator) doCheckBuildInRules( // Values of two fields should be equal as string. case "same": if v, ok := dataMap[rulePattern]; ok { - if strings.Compare(valueStr, v) == 0 { + if strings.Compare(valueStr, gconv.String(v)) == 0 { match = true } } @@ -250,7 +248,7 @@ func (v *Validator) doCheckBuildInRules( case "different": match = true if v, ok := dataMap[rulePattern]; ok { - if strings.Compare(valueStr, v) == 0 { + if strings.Compare(valueStr, gconv.String(v)) == 0 { match = false } } diff --git a/util/gvalid/gvalid_validator_rule_required.go b/util/gvalid/gvalid_validator_rule_required.go index c86e1c8fd..969d7bb56 100644 --- a/util/gvalid/gvalid_validator_rule_required.go +++ b/util/gvalid/gvalid_validator_rule_required.go @@ -7,6 +7,7 @@ package gvalid import ( + "github.com/gogf/gf/internal/empty" "github.com/gogf/gf/util/gconv" "reflect" "strings" @@ -14,7 +15,7 @@ import ( // checkRequired checks `value` using required rules. // It also supports require checks for `value` of type: slice, map. -func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, params map[string]string) bool { +func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string, dataMap map[string]interface{}) bool { required := false switch ruleKey { // Required. @@ -31,8 +32,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string for i := 0; i < len(array); { tk := array[i] tv := array[i+1] - if v, ok := params[tk]; ok { - if strings.Compare(tv, v) == 0 { + if v, ok := dataMap[tk]; ok { + if strings.Compare(tv, gconv.String(v)) == 0 { required = true break } @@ -51,8 +52,8 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string for i := 0; i < len(array); { tk := array[i] tv := array[i+1] - if v, ok := params[tk]; ok { - if strings.Compare(tv, v) == 0 { + if v, ok := dataMap[tk]; ok { + if strings.Compare(tv, gconv.String(v)) == 0 { required = false break } @@ -67,7 +68,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = false array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] != "" { + if !empty.IsEmpty(dataMap[array[i]]) { required = true break } @@ -79,7 +80,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = true array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] == "" { + if empty.IsEmpty(dataMap[array[i]]) { required = false break } @@ -91,7 +92,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = false array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] == "" { + if empty.IsEmpty(dataMap[array[i]]) { required = true break } @@ -103,7 +104,7 @@ func (v *Validator) checkRequired(value interface{}, ruleKey, rulePattern string required = true array := strings.Split(rulePattern, ",") for i := 0; i < len(array); i++ { - if params[array[i]] != "" { + if !empty.IsEmpty(dataMap[array[i]]) { required = false break } diff --git a/util/gvalid/gvalid_z_unit_basic_all_test.go b/util/gvalid/gvalid_z_unit_basic_all_test.go index 25ffc0b65..bfde55995 100755 --- a/util/gvalid/gvalid_z_unit_basic_all_test.go +++ b/util/gvalid/gvalid_z_unit_basic_all_test.go @@ -88,6 +88,72 @@ func Test_RequiredWith(t *testing.T) { t.AssertNE(err2, nil) t.AssertNE(err3, nil) }) + // time.Time + gtest.C(t, func(t *gtest.T) { + rule := "required-with:id,time" + val1 := "" + params1 := g.Map{ + "age": 18, + } + params2 := g.Map{ + "id": 100, + } + params3 := g.Map{ + "time": time.Time{}, + } + err1 := gvalid.Check(val1, rule, nil, params1) + err2 := gvalid.Check(val1, rule, nil, params2) + err3 := gvalid.Check(val1, rule, nil, params3) + t.Assert(err1, nil) + t.AssertNE(err2, nil) + t.Assert(err3, nil) + }) + gtest.C(t, func(t *gtest.T) { + rule := "required-with:id,time" + val1 := "" + params1 := g.Map{ + "age": 18, + } + params2 := g.Map{ + "id": 100, + } + params3 := g.Map{ + "time": time.Now(), + } + err1 := gvalid.Check(val1, rule, nil, params1) + err2 := gvalid.Check(val1, rule, nil, params2) + err3 := gvalid.Check(val1, rule, nil, params3) + t.Assert(err1, nil) + t.AssertNE(err2, nil) + t.AssertNE(err3, nil) + }) + // gtime.Time + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid"` + Nickname string `json:"nickname" v:"required-with:Uid"` + StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` + EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` + } + data := UserApiSearch{ + StartTime: nil, + EndTime: nil, + } + t.Assert(gvalid.CheckStruct(data, nil), nil) + }) + gtest.C(t, func(t *gtest.T) { + type UserApiSearch struct { + Uid int64 `json:"uid"` + Nickname string `json:"nickname" v:"required-with:Uid"` + StartTime *gtime.Time `json:"start_time" v:"required-with:EndTime"` + EndTime *gtime.Time `json:"end_time" v:"required-with:StartTime"` + } + data := UserApiSearch{ + StartTime: nil, + EndTime: gtime.Now(), + } + t.AssertNE(gvalid.CheckStruct(data, nil), nil) + }) } func Test_RequiredWithAll(t *testing.T) {