github.com/wangyougui/gf/v2@v2.6.5/util/gvalid/gvalid_validator_check_map.go (about)

     1  // Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/wangyougui/gf.
     6  
     7  package gvalid
     8  
     9  import (
    10  	"context"
    11  	"errors"
    12  	"reflect"
    13  	"strings"
    14  
    15  	"github.com/wangyougui/gf/v2/errors/gcode"
    16  	"github.com/wangyougui/gf/v2/internal/reflection"
    17  	"github.com/wangyougui/gf/v2/util/gconv"
    18  )
    19  
    20  func (v *Validator) doCheckMap(ctx context.Context, params interface{}) Error {
    21  	if params == nil {
    22  		return nil
    23  	}
    24  	var (
    25  		checkRules    = make([]fieldRule, 0)
    26  		customMessage = make(CustomMsg) // map[RuleKey]ErrorMsg.
    27  		errorMaps     = make(map[string]map[string]error)
    28  	)
    29  	switch assertValue := v.rules.(type) {
    30  	// Sequence tag: []sequence tag
    31  	// Sequence has order for error results.
    32  	case []string:
    33  		for _, tag := range assertValue {
    34  			name, rule, msg := ParseTagValue(tag)
    35  			if len(name) == 0 {
    36  				continue
    37  			}
    38  			if len(msg) > 0 {
    39  				var (
    40  					msgArray  = strings.Split(msg, "|")
    41  					ruleArray = strings.Split(rule, "|")
    42  				)
    43  				for k, ruleItem := range ruleArray {
    44  					// If length of custom messages is lesser than length of rules,
    45  					// the rest rules use the default error messages.
    46  					if len(msgArray) <= k {
    47  						continue
    48  					}
    49  					if len(msgArray[k]) == 0 {
    50  						continue
    51  					}
    52  					array := strings.Split(ruleItem, ":")
    53  					if _, ok := customMessage[name]; !ok {
    54  						customMessage[name] = make(map[string]string)
    55  					}
    56  					customMessage[name].(map[string]string)[strings.TrimSpace(array[0])] = strings.TrimSpace(msgArray[k])
    57  				}
    58  			}
    59  			checkRules = append(checkRules, fieldRule{
    60  				Name: name,
    61  				Rule: rule,
    62  			})
    63  		}
    64  
    65  	// No sequence rules: map[field]rule
    66  	case map[string]string:
    67  		for name, rule := range assertValue {
    68  			checkRules = append(checkRules, fieldRule{
    69  				Name: name,
    70  				Rule: rule,
    71  			})
    72  		}
    73  	}
    74  	inputParamMap := gconv.Map(params)
    75  	if inputParamMap == nil {
    76  		return newValidationErrorByStr(
    77  			internalParamsErrRuleName,
    78  			errors.New("invalid params type: convert to map failed"),
    79  		)
    80  	}
    81  	if msg, ok := v.messages.(CustomMsg); ok && len(msg) > 0 {
    82  		if len(customMessage) > 0 {
    83  			for k, v := range msg {
    84  				customMessage[k] = v
    85  			}
    86  		} else {
    87  			customMessage = msg
    88  		}
    89  	}
    90  	var (
    91  		value     interface{}
    92  		validator = v.Clone()
    93  	)
    94  
    95  	// It checks the struct recursively if its attribute is an embedded struct.
    96  	// Ignore inputParamMap, assoc, rules and messages from parent.
    97  	validator.assoc = nil
    98  	validator.rules = nil
    99  	validator.messages = nil
   100  	for _, item := range inputParamMap {
   101  		originTypeAndKind := reflection.OriginTypeAndKind(item)
   102  		switch originTypeAndKind.OriginKind {
   103  		case reflect.Map, reflect.Struct, reflect.Slice, reflect.Array:
   104  			v.doCheckValueRecursively(ctx, doCheckValueRecursivelyInput{
   105  				Value:     item,
   106  				Type:      originTypeAndKind.InputType,
   107  				Kind:      originTypeAndKind.OriginKind,
   108  				ErrorMaps: errorMaps,
   109  			})
   110  		}
   111  		// Bail feature.
   112  		if v.bail && len(errorMaps) > 0 {
   113  			break
   114  		}
   115  	}
   116  	if v.bail && len(errorMaps) > 0 {
   117  		return newValidationError(gcode.CodeValidationFailed, nil, errorMaps)
   118  	}
   119  
   120  	// The following logic is the same as some of CheckStruct but without sequence support.
   121  	for _, checkRuleItem := range checkRules {
   122  		if len(checkRuleItem.Rule) == 0 {
   123  			continue
   124  		}
   125  		value = nil
   126  		if valueItem, ok := inputParamMap[checkRuleItem.Name]; ok {
   127  			value = valueItem
   128  		}
   129  		// It checks each rule and its value in loop.
   130  		if validatedError := v.doCheckValue(ctx, doCheckValueInput{
   131  			Name:      checkRuleItem.Name,
   132  			Value:     value,
   133  			ValueType: reflect.TypeOf(value),
   134  			Rule:      checkRuleItem.Rule,
   135  			Messages:  customMessage[checkRuleItem.Name],
   136  			DataRaw:   params,
   137  			DataMap:   inputParamMap,
   138  		}); validatedError != nil {
   139  			_, errorItem := validatedError.FirstItem()
   140  			// ===========================================================
   141  			// Only in map and struct validations:
   142  			// If value is nil or empty string and has no required* rules,
   143  			// it clears the error message.
   144  			// ===========================================================
   145  			if gconv.String(value) == "" {
   146  				required := false
   147  				// rule => error
   148  				for ruleKey := range errorItem {
   149  					if required = v.checkRuleRequired(ruleKey); required {
   150  						break
   151  					}
   152  				}
   153  				if !required {
   154  					continue
   155  				}
   156  			}
   157  			if _, ok := errorMaps[checkRuleItem.Name]; !ok {
   158  				errorMaps[checkRuleItem.Name] = make(map[string]error)
   159  			}
   160  			for ruleKey, ruleError := range errorItem {
   161  				errorMaps[checkRuleItem.Name][ruleKey] = ruleError
   162  			}
   163  			if v.bail {
   164  				break
   165  			}
   166  		}
   167  	}
   168  	if len(errorMaps) > 0 {
   169  		return newValidationError(gcode.CodeValidationFailed, checkRules, errorMaps)
   170  	}
   171  	return nil
   172  }