// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. // // This Source Code Form is subject to the terms of the MIT License. // If a copy of the MIT was not distributed with this file, // You can obtain one at https://github.com/gogf/gf. package gvalid import ( "context" "errors" "reflect" "strings" "github.com/gogf/gf/v2/errors/gcode" "github.com/gogf/gf/v2/internal/reflection" "github.com/gogf/gf/v2/util/gconv" ) func (v *Validator) doCheckMap(ctx context.Context, params interface{}) Error { if params == nil { return nil } var ( checkRules = make([]fieldRule, 0) customMessage = make(CustomMsg) // map[RuleKey]ErrorMsg. errorMaps = make(map[string]map[string]error) ) switch assertValue := v.rules.(type) { // Sequence tag: []sequence tag // Sequence has order for error results. case []string: for _, tag := range assertValue { name, rule, msg := ParseTagValue(tag) if len(name) == 0 { continue } if len(msg) > 0 { var ( msgArray = strings.Split(msg, "|") ruleArray = strings.Split(rule, "|") ) for k, ruleItem := range ruleArray { // If length of custom messages is lesser than length of rules, // the rest rules use the default error messages. if len(msgArray) <= k { continue } if len(msgArray[k]) == 0 { continue } array := strings.Split(ruleItem, ":") if _, ok := customMessage[name]; !ok { customMessage[name] = make(map[string]string) } customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k]) } } checkRules = append(checkRules, fieldRule{ Name: name, Rule: rule, }) } // No sequence rules: map[field]rule case map[string]string: for name, rule := range assertValue { checkRules = append(checkRules, fieldRule{ Name: name, Rule: rule, }) } } inputParamMap := gconv.Map(params) if inputParamMap == nil { return newValidationErrorByStr( internalParamsErrRuleName, errors.New("invalid params type: convert to map failed"), ) } if msg, ok := v.messages.(CustomMsg); ok && len(msg) > 0 { if len(customMessage) > 0 { for k, v := range msg { customMessage[k] = v } } else { customMessage = msg } } var ( value interface{} validator = v.Clone() ) // It checks the struct recursively if its attribute is an embedded struct. // Ignore inputParamMap, assoc, rules and messages from parent. validator.assoc = nil validator.rules = nil validator.messages = nil for _, item := range inputParamMap { originTypeAndKind := reflection.OriginTypeAndKind(item) switch originTypeAndKind.OriginKind { case reflect.Map, reflect.Struct, reflect.Slice, reflect.Array: v.doCheckValueRecursively(ctx, doCheckValueRecursivelyInput{ Value: item, Type: originTypeAndKind.InputType, Kind: originTypeAndKind.OriginKind, ErrorMaps: errorMaps, }) } // Bail feature. if v.bail && len(errorMaps) > 0 { break } } if v.bail && len(errorMaps) > 0 { return newValidationError(gcode.CodeValidationFailed, nil, errorMaps) } // The following logic is the same as some of CheckStruct but without sequence support. for _, checkRuleItem := range checkRules { if len(checkRuleItem.Rule) == 0 { continue } value = nil if valueItem, ok := inputParamMap[checkRuleItem.Name]; ok { value = valueItem } // It checks each rule and its value in loop. if validatedError := v.doCheckValue(ctx, doCheckValueInput{ Name: checkRuleItem.Name, Value: value, ValueType: reflect.TypeOf(value), Rule: checkRuleItem.Rule, Messages: customMessage[checkRuleItem.Name], DataRaw: params, DataMap: inputParamMap, }); validatedError != nil { _, errorItem := validatedError.FirstItem() // =========================================================== // Only in map and struct validations: // If value is nil or empty string and has no required* rules, // it clears the error message. // =========================================================== if gconv.String(value) == "" { required := false // rule => error for ruleKey := range errorItem { if required = v.checkRuleRequired(ruleKey); required { break } } if !required { continue } } if _, ok := errorMaps[checkRuleItem.Name]; !ok { errorMaps[checkRuleItem.Name] = make(map[string]error) } for ruleKey, ruleError := range errorItem { errorMaps[checkRuleItem.Name][ruleKey] = ruleError } if v.bail { break } } } if len(errorMaps) > 0 { return newValidationError(gcode.CodeValidationFailed, checkRules, errorMaps) } return nil }