Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refract builtin rules management mechanism, add eq/not-eq/gt/gte/lt/lte/before/before-equal/after/after-equal/array/not-regex rules for for package gvalid #2133

Merged
merged 9 commits into from
Sep 21, 2022
2 changes: 2 additions & 0 deletions database/gdb/gdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -421,6 +421,7 @@ func NewByGroup(group ...string) (db DB, err error) {

func parseConfigNodeLink(node ConfigNode) ConfigNode {
var match []string
// It firstly parses `link` using with type pattern.
match, _ = gregex.MatchString(linkPatternWithType, node.Link)
if len(match) > 6 {
node.Type = match[1]
Expand All @@ -435,6 +436,7 @@ func parseConfigNodeLink(node ConfigNode) ConfigNode {
}
node.Link = ""
} else {
// Else it parses `link` using without type pattern.
match, _ = gregex.MatchString(linkPatternWithoutType, node.Link)
if len(match) > 6 {
node.User = match[1]
Expand Down
2 changes: 1 addition & 1 deletion database/gdb/gdb_core_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ type ConfigNode struct {
Pass string `json:"pass"` // Authentication password.
Name string `json:"name"` // Default used database name.
Type string `json:"type"` // Database type: mysql, sqlite, mssql, pgsql, oracle.
Link string `json:"link"` // (Optional) Custom link information, when it is used, configuration Host/Port/User/Pass/Name are ignored.
Link string `json:"link"` // (Optional) Custom link information for all configuration in one single string.
Extra string `json:"extra"` // (Optional) Extra configuration according the registered third-party database driver.
Role string `json:"role"` // (Optional, "master" in default) Node role, used for master-slave mode: master, slave.
Debug bool `json:"debug"` // (Optional) Debug mode enables debug information logging and output.
Expand Down
2 changes: 1 addition & 1 deletion frame/gins/gins_database.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ func Database(name ...string) gdb.DB {
if fileConfig, ok := Config().GetAdapter().(*gcfg.AdapterFile); ok {
if _, err = fileConfig.GetFilePath(); err != nil {
panic(gerror.WrapCode(gcode.CodeMissingConfiguration, err,
`configuration not found, did you miss the configuration file or the misspell the configuration file name`,
`configuration not found, did you miss the configuration file or misspell the configuration file name`,
))
}
}
Expand Down
140 changes: 8 additions & 132 deletions util/gvalid/gvalid.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,144 +43,20 @@ const (
internalDefaultRuleName = "__default__" // default rule name for i18n error message format if no i18n message found for specified error rule.
ruleMessagePrefixForI18n = "gf.gvalid.rule." // prefix string for each rule configuration in i18n content.
noValidationTagName = "nv" // no validation tag name for struct attribute.
ruleNameRegex = "regex" // the name for rule "regex"
ruleNameNotRegex = "not-regex" // the name for rule "not-regex"
ruleNameBail = "bail" // the name for rule "bail"
ruleNameCi = "ci" // the name for rule "ci"
emptyJsonArrayStr = "[]" // Empty json string for array type.
emptyJsonObjectStr = "{}" // Empty json string for object type.
requiredRulesPrefix = "required" // requiredRulesPrefix specifies the rule prefix that must be validated even the value is empty (nil or empty).
)

var (
// allSupportedRules defines all supported rules that is used for quick checks.
// Refer to Laravel validation:
// https://laravel.com/docs/5.5/validation#available-validation-rules
// https://learnku.com/docs/laravel/5.4/validation
allSupportedRules = map[string]struct{}{
"required": {}, // format: required brief: Required.
"required-if": {}, // format: required-if:field,value,... brief: Required unless all given field and its value are equal.
"required-unless": {}, // format: required-unless:field,value,... brief: Required unless all given field and its value are not equal.
"required-with": {}, // format: required-with:field1,field2,... brief: Required if any of given fields are not empty.
"required-with-all": {}, // format: required-with-all:field1,field2,... brief: Required if all given fields are not empty.
"required-without": {}, // format: required-without:field1,field2,... brief: Required if any of given fields are empty.
"required-without-all": {}, // format: required-without-all:field1,field2,...brief: Required if all given fields are empty.
"bail": {}, // format: bail brief: Stop validating when this field's validation failed.
"ci": {}, // format: ci brief: Case-Insensitive configuration for those rules that need value comparison like: same, different, in, not-in, etc.
"date": {}, // format: date brief: Standard date, like: 2006-01-02, 20060102, 2006.01.02
"datetime": {}, // format: datetime brief: Standard datetime, like: 2006-01-02 12:00:00
"date-format": {}, // format: date-format:format brief: Custom date format.
"email": {}, // format: email brief: Email address.
"phone": {}, // format: phone brief: Phone number.
"phone-loose": {}, // format: phone-loose brief: Loose phone number validation.
"telephone": {}, // format: telephone brief: Telephone number, like: "XXXX-XXXXXXX"、"XXXX-XXXXXXXX"、"XXX-XXXXXXX"、"XXX-XXXXXXXX"、"XXXXXXX"、"XXXXXXXX"
"passport": {}, // format: passport brief: Universal passport format rule: Starting with letter, containing only numbers or underscores, length between 6 and 18
"password": {}, // format: password brief: Universal password format rule1: Containing any visible chars, length between 6 and 18.
"password2": {}, // format: password2 brief: Universal password format rule2: Must meet password rule1, must contain lower and upper letters and numbers.
"password3": {}, // format: password3 brief: Universal password format rule3: Must meet password rule1, must contain lower and upper letters, numbers and special chars.
"postcode": {}, // format: postcode brief: Postcode number.
"resident-id": {}, // format: resident-id brief: Resident id number.
"bank-card": {}, // format: bank-card brief: Bank card number.
"qq": {}, // format: qq brief: Tencent QQ number.
"ip": {}, // format: ip brief: IPv4/IPv6.
"ipv4": {}, // format: ipv4 brief: IPv4.
"ipv6": {}, // format: ipv6 brief: IPv6.
"mac": {}, // format: mac brief: MAC.
"url": {}, // format: url brief: URL.
"domain": {}, // format: domain brief: Domain.
"length": {}, // format: length:min,max brief: Length between :min and :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
"min-length": {}, // format: min-length:min brief: Length is equal or greater than :min. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
"max-length": {}, // format: max-length:max brief: Length is equal or lesser than :max. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
"size": {}, // format: size:size brief: Length must be :size. The length is calculated using unicode string, which means one chinese character or letter both has the length of 1.
"between": {}, // format: between:min,max brief: Range between :min and :max. It supports both integer and float.
"min": {}, // format: min:min brief: Equal or greater than :min. It supports both integer and float.
"max": {}, // format: max:max brief: Equal or lesser than :max. It supports both integer and float.
"json": {}, // format: json brief: JSON.
"integer": {}, // format: integer brief: Integer.
"float": {}, // format: float brief: Float. Note that an integer is actually a float number.
"boolean": {}, // format: boolean brief: Boolean(1,true,on,yes:true | 0,false,off,no,"":false)
"same": {}, // format: same:field brief: Value should be the same as value of field.
"different": {}, // format: different:field brief: Value should be different from value of field.
"in": {}, // format: in:value1,value2,... brief: Value should be in: value1,value2,...
"not-in": {}, // format: not-in:value1,value2,... brief: Value should not be in: value1,value2,...
"regex": {}, // format: regex:pattern brief: Value should match custom regular expression pattern.
}

// defaultMessages is the default error messages.
// defaultErrorMessages is the default error messages.
// Note that these messages are synchronized from ./i18n/en/validation.toml .
defaultMessages = map[string]string{
"required": "The {attribute} field is required",
"required-if": "The {attribute} field is required",
"required-unless": "The {attribute} field is required",
"required-with": "The {attribute} field is required",
"required-with-all": "The {attribute} field is required",
"required-without": "The {attribute} field is required",
"required-without-all": "The {attribute} field is required",
"date": "The {attribute} value `{value}` is not a valid date",
"datetime": "The {attribute} value `{value}` is not a valid datetime",
"date-format": "The {attribute} value `{value}` does not match the format: {pattern}",
"email": "The {attribute} value `{value}` is not a valid email address",
"phone": "The {attribute} value `{value}` is not a valid phone number",
"telephone": "The {attribute} value `{value}` is not a valid telephone number",
"passport": "The {attribute} value `{value}` is not a valid passport format",
"password": "The {attribute} value `{value}` is not a valid password format",
"password2": "The {attribute} value `{value}` is not a valid password format",
"password3": "The {attribute} value `{value}` is not a valid password format",
"postcode": "The {attribute} value `{value}` is not a valid postcode format",
"resident-id": "The {attribute} value `{value}` is not a valid resident id number",
"bank-card": "The {attribute} value `{value}` is not a valid bank card number",
"qq": "The {attribute} value `{value}` is not a valid QQ number",
"ip": "The {attribute} value `{value}` is not a valid IP address",
"ipv4": "The {attribute} value `{value}` is not a valid IPv4 address",
"ipv6": "The {attribute} value `{value}` is not a valid IPv6 address",
"mac": "The {attribute} value `{value}` is not a valid MAC address",
"url": "The {attribute} value `{value}` is not a valid URL address",
"domain": "The {attribute} value `{value}` is not a valid domain format",
"length": "The {attribute} value `{value}` length must be between {min} and {max}",
"min-length": "The {attribute} value `{value}` length must be equal or greater than {min}",
"max-length": "The {attribute} value `{value}` length must be equal or lesser than {max}",
"size": "The {attribute} value `{value}` length must be {size}",
"between": "The {attribute} value `{value}` must be between {min} and {max}",
"min": "The {attribute} value `{value}` must be equal or greater than {min}",
"max": "The {attribute} value `{value}` must be equal or lesser than {max}",
"json": "The {attribute} value `{value}` is not a valid JSON string",
"xml": "The {attribute} value `{value}` is not a valid XML string",
"array": "The {attribute} value `{value}` is not an array",
"integer": "The {attribute} value `{value}` is not an integer",
"boolean": "The {attribute} value `{value}` field must be true or false",
"same": "The {attribute} value `{value}` must be the same as field {pattern}",
"different": "The {attribute} value `{value}` must be different from field {pattern}",
"in": "The {attribute} value `{value}` is not in acceptable range: {pattern}",
"not-in": "The {attribute} value `{value}` must not be in range: {pattern}",
"regex": "The {attribute} value `{value}` must be in regex of: {pattern}",
internalDefaultRuleName: "The {attribute} value `{value}` is invalid",
}

// mustCheckRulesEvenValueEmpty specifies some rules that must be validated
// even the value is empty (nil or empty).
mustCheckRulesEvenValueEmpty = map[string]struct{}{
"required": {},
"required-if": {},
"required-unless": {},
"required-with": {},
"required-with-all": {},
"required-without": {},
"required-without-all": {},
//"same": {},
//"different": {},
//"in": {},
//"not-in": {},
//"regex": {},
}

// boolMap defines the boolean values.
boolMap = map[string]struct{}{
"1": {},
"true": {},
"on": {},
"yes": {},
"": {},
"0": {},
"false": {},
"off": {},
"no": {},
defaultErrorMessages = map[string]string{
internalDefaultRuleName: "The {field} value `{value}` is invalid",
}

structTagPriority = []string{"gvalid", "valid", "v"} // structTagPriority specifies the validation tag priority array.
Expand All @@ -196,9 +72,9 @@ var (
// which is compiled just once and of repeatable usage.
ruleRegex, _ = regexp.Compile(singleRulePattern)

// markedRuleMap defines all rules that are just marked rules which have neither functional meaning
// decorativeRuleMap defines all rules that are just marked rules which have neither functional meaning
// nor error messages.
markedRuleMap = map[string]bool{
decorativeRuleMap = map[string]bool{
ruleNameBail: true,
ruleNameCi: true,
}
Expand Down
File renamed without changes.
18 changes: 16 additions & 2 deletions util/gvalid/gvalid_validator.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"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"
)

Expand Down Expand Up @@ -177,11 +178,24 @@ func (v *Validator) RuleFuncMap(m map[string]RuleFunc) *Validator {
return newValidator
}

// getRuleFunc retrieves and returns the custom rule function for specified rule.
func (v *Validator) getRuleFunc(rule string) RuleFunc {
// 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
}
4 changes: 1 addition & 3 deletions util/gvalid/gvalid_validator_check_map.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,9 +145,7 @@ func (v *Validator) doCheckMap(ctx context.Context, params interface{}) Error {
required := false
// rule => error
for ruleKey := range errorItem {
// Default required rules.
if _, ok := mustCheckRulesEvenValueEmpty[ruleKey]; ok {
required = true
if required = v.checkRuleRequired(ruleKey); required {
break
}
}
Expand Down
9 changes: 1 addition & 8 deletions util/gvalid/gvalid_validator_check_struct.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,14 +311,7 @@ func (v *Validator) doCheckStruct(ctx context.Context, object interface{}) Error
required := false
// rule => error
for ruleKey := range errorItem {
// Default required rules.
if _, ok := mustCheckRulesEvenValueEmpty[ruleKey]; ok {
required = true
break
}
// All custom validation rules are required rules.
if _, ok := customRuleFuncMap[ruleKey]; ok {
required = true
if required = v.checkRuleRequired(ruleKey); required {
break
}
}
Expand Down
Loading