mirror of
https://gitee.com/johng/gf.git
synced 2024-12-02 04:07:47 +08:00
212 lines
6.9 KiB
Go
212 lines
6.9 KiB
Go
// 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"
|
|
|
|
"github.com/gogf/gf/v2/i18n/gi18n"
|
|
"github.com/gogf/gf/v2/internal/reflection"
|
|
"github.com/gogf/gf/v2/internal/utils"
|
|
"github.com/gogf/gf/v2/text/gstr"
|
|
"github.com/gogf/gf/v2/util/gconv"
|
|
)
|
|
|
|
// Validator is the validation manager for chaining operations.
|
|
type Validator struct {
|
|
i18nManager *gi18n.Manager // I18n manager for error message translation.
|
|
data interface{} // Validation data, which can be a map, struct or a certain value to be validated.
|
|
assoc interface{} // Associated data, which is usually a map, for union validation.
|
|
rules interface{} // Custom validation data.
|
|
messages interface{} // Custom validation error messages, which can be string or type of CustomMsg.
|
|
ruleFuncMap map[string]RuleFunc // ruleFuncMap stores custom rule functions for current Validator.
|
|
useAssocInsteadOfObjectAttributes bool // Using `assoc` as its validation source instead of attribute values from `Object`.
|
|
bail bool // Stop validation after the first validation error.
|
|
foreach bool // It tells the next validation using current value as an array and validates each of its element.
|
|
caseInsensitive bool // Case-Insensitive configuration for those rules that need value comparison.
|
|
}
|
|
|
|
// New creates and returns a new Validator.
|
|
func New() *Validator {
|
|
return &Validator{
|
|
i18nManager: gi18n.Instance(), // Use default i18n manager.
|
|
ruleFuncMap: make(map[string]RuleFunc), // Custom rule function storing map.
|
|
}
|
|
}
|
|
|
|
// Run starts validating the given data with rules and messages.
|
|
func (v *Validator) Run(ctx context.Context) Error {
|
|
if v.data == nil {
|
|
return newValidationErrorByStr(
|
|
internalParamsErrRuleName,
|
|
errors.New(`no data passed for validation`),
|
|
)
|
|
}
|
|
|
|
originValueAndKind := reflection.OriginValueAndKind(v.data)
|
|
switch originValueAndKind.OriginKind {
|
|
case reflect.Map:
|
|
isMapValidation := false
|
|
if v.rules == nil {
|
|
isMapValidation = true
|
|
} else if utils.IsMap(v.rules) || utils.IsSlice(v.rules) {
|
|
isMapValidation = true
|
|
}
|
|
if isMapValidation {
|
|
return v.doCheckMap(ctx, v.data)
|
|
}
|
|
|
|
case reflect.Struct:
|
|
isStructValidation := false
|
|
if v.rules == nil {
|
|
isStructValidation = true
|
|
} else if utils.IsMap(v.rules) || utils.IsSlice(v.rules) {
|
|
isStructValidation = true
|
|
}
|
|
if isStructValidation {
|
|
return v.doCheckStruct(ctx, v.data)
|
|
}
|
|
}
|
|
|
|
return v.doCheckValue(ctx, doCheckValueInput{
|
|
Name: "",
|
|
Value: v.data,
|
|
ValueType: reflect.TypeOf(v.data),
|
|
Rule: gconv.String(v.rules),
|
|
Messages: v.messages,
|
|
DataRaw: v.assoc,
|
|
DataMap: gconv.Map(v.assoc),
|
|
})
|
|
}
|
|
|
|
// Clone creates and returns a new Validator which is a shallow copy of current one.
|
|
func (v *Validator) Clone() *Validator {
|
|
newValidator := New()
|
|
*newValidator = *v
|
|
return newValidator
|
|
}
|
|
|
|
// I18n sets the i18n manager for the validator.
|
|
func (v *Validator) I18n(i18nManager *gi18n.Manager) *Validator {
|
|
if i18nManager == nil {
|
|
return v
|
|
}
|
|
newValidator := v.Clone()
|
|
newValidator.i18nManager = i18nManager
|
|
return newValidator
|
|
}
|
|
|
|
// Bail sets the mark for stopping validation after the first validation error.
|
|
func (v *Validator) Bail() *Validator {
|
|
newValidator := v.Clone()
|
|
newValidator.bail = true
|
|
return newValidator
|
|
}
|
|
|
|
// Foreach tells the next validation using current value as an array and validates each of its element.
|
|
// Note that this decorating rule takes effect just once for next validation rule, specially for single value validation.
|
|
func (v *Validator) Foreach() *Validator {
|
|
newValidator := v.Clone()
|
|
newValidator.foreach = true
|
|
return newValidator
|
|
}
|
|
|
|
// 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.
|
|
func (v *Validator) Data(data interface{}) *Validator {
|
|
if data == nil {
|
|
return v
|
|
}
|
|
newValidator := v.Clone()
|
|
newValidator.data = data
|
|
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 `useAssocInsteadOfObjectAttributes` true
|
|
func (v *Validator) Assoc(assoc interface{}) *Validator {
|
|
if assoc == nil {
|
|
return v
|
|
}
|
|
newValidator := v.Clone()
|
|
newValidator.assoc = assoc
|
|
newValidator.useAssocInsteadOfObjectAttributes = true
|
|
return newValidator
|
|
}
|
|
|
|
// Rules is a chaining operation function, which sets custom validation rules for current operation.
|
|
func (v *Validator) Rules(rules interface{}) *Validator {
|
|
if rules == nil {
|
|
return v
|
|
}
|
|
newValidator := v.Clone()
|
|
newValidator.rules = rules
|
|
return newValidator
|
|
}
|
|
|
|
// Messages is a chaining operation function, which sets custom error messages for current operation.
|
|
// The parameter `messages` can be type of string/[]string/map[string]string. It supports sequence in error result
|
|
// if `rules` is type of []string.
|
|
func (v *Validator) Messages(messages interface{}) *Validator {
|
|
if messages == nil {
|
|
return v
|
|
}
|
|
newValidator := v.Clone()
|
|
newValidator.messages = messages
|
|
return newValidator
|
|
}
|
|
|
|
// RuleFunc registers one custom rule function to current Validator.
|
|
func (v *Validator) RuleFunc(rule string, f RuleFunc) *Validator {
|
|
newValidator := v.Clone()
|
|
newValidator.ruleFuncMap[rule] = f
|
|
return newValidator
|
|
}
|
|
|
|
// RuleFuncMap registers multiple custom rule functions to current Validator.
|
|
func (v *Validator) RuleFuncMap(m map[string]RuleFunc) *Validator {
|
|
if m == nil {
|
|
return v
|
|
}
|
|
newValidator := v.Clone()
|
|
for k, v := range m {
|
|
newValidator.ruleFuncMap[k] = v
|
|
}
|
|
return newValidator
|
|
}
|
|
|
|
// getCustomRuleFunc retrieves and returns the custom rule function for specified rule.
|
|
func (v *Validator) getCustomRuleFunc(rule string) RuleFunc {
|
|
ruleFunc := v.ruleFuncMap[rule]
|
|
if ruleFunc == nil {
|
|
ruleFunc = customRuleFuncMap[rule]
|
|
}
|
|
return ruleFunc
|
|
}
|
|
|
|
// checkRuleRequired checks and returns whether the given `rule` is required even it is nil or empty.
|
|
func (v *Validator) checkRuleRequired(rule string) bool {
|
|
// Default required rules.
|
|
if gstr.HasPrefix(rule, requiredRulesPrefix) {
|
|
return true
|
|
}
|
|
// All custom validation rules are required rules.
|
|
if _, ok := customRuleFuncMap[rule]; ok {
|
|
return true
|
|
}
|
|
return false
|
|
}
|