github.com/goravel/framework@v1.13.9/validation/validation.go (about) 1 package validation 2 3 import ( 4 "errors" 5 "reflect" 6 "time" 7 8 "github.com/gookit/validate" 9 10 "github.com/goravel/framework/contracts/http" 11 validatecontract "github.com/goravel/framework/contracts/validation" 12 ) 13 14 type Validation struct { 15 rules []validatecontract.Rule 16 } 17 18 func NewValidation() *Validation { 19 return &Validation{ 20 rules: make([]validatecontract.Rule, 0), 21 } 22 } 23 24 func (r *Validation) Make(data any, rules map[string]string, options ...validatecontract.Option) (validatecontract.Validator, error) { 25 if data == nil { 26 return nil, errors.New("data can't be empty") 27 } 28 if len(rules) == 0 { 29 return nil, errors.New("rules can't be empty") 30 } 31 32 var dataType reflect.Kind 33 switch data := data.(type) { 34 case map[string]any: 35 if len(data) == 0 { 36 return nil, errors.New("data can't be empty") 37 } 38 dataType = reflect.Map 39 } 40 41 val := reflect.ValueOf(data) 42 indirectVal := reflect.Indirect(val) 43 typ := indirectVal.Type() 44 if indirectVal.Kind() == reflect.Struct && typ != reflect.TypeOf(time.Time{}) { 45 dataType = reflect.Struct 46 } 47 48 var dataFace validate.DataFace 49 switch dataType { 50 case reflect.Map: 51 dataFace = validate.FromMap(data.(map[string]any)) 52 case reflect.Struct: 53 var err error 54 dataFace, err = validate.FromStruct(data) 55 if err != nil { 56 return nil, err 57 } 58 default: 59 return nil, errors.New("data must be map[string]any or struct") 60 } 61 62 options = append(options, Rules(rules), CustomRules(r.rules)) 63 generateOptions := GenerateOptions(options) 64 if generateOptions["prepareForValidation"] != nil { 65 if err := generateOptions["prepareForValidation"].(func(ctx http.Context, data validatecontract.Data) error)(nil, NewData(dataFace)); err != nil { 66 return nil, err 67 } 68 } 69 70 v := dataFace.Create() 71 AppendOptions(v, generateOptions) 72 73 return NewValidator(v, dataFace), nil 74 } 75 76 func (r *Validation) AddRules(rules []validatecontract.Rule) error { 77 existRuleNames := r.existRuleNames() 78 for _, rule := range rules { 79 for _, existRuleName := range existRuleNames { 80 if existRuleName == rule.Signature() { 81 return errors.New("duplicate rule name: " + rule.Signature()) 82 } 83 } 84 } 85 86 r.rules = append(r.rules, rules...) 87 88 return nil 89 } 90 91 func (r *Validation) Rules() []validatecontract.Rule { 92 return r.rules 93 } 94 95 func (r *Validation) existRuleNames() []string { 96 rules := []string{ 97 "required", 98 "required_if", 99 "requiredIf", 100 "required_unless", 101 "requiredUnless", 102 "required_with", 103 "requiredWith", 104 "required_with_all", 105 "requiredWithAll", 106 "required_without", 107 "requiredWithout", 108 "required_without_all", 109 "requiredWithoutAll", 110 "safe", 111 "int", 112 "integer", 113 "isInt", 114 "uint", 115 "isUint", 116 "bool", 117 "isBool", 118 "string", 119 "isString", 120 "float", 121 "isFloat", 122 "slice", 123 "isSlice", 124 "in", 125 "enum", 126 "not_in", 127 "notIn", 128 "contains", 129 "not_contains", 130 "notContains", 131 "string_contains", 132 "stringContains", 133 "starts_with", 134 "startsWith", 135 "ends_with", 136 "endsWith", 137 "range", 138 "between", 139 "max", 140 "lte", 141 "min", 142 "gte", 143 "eq", 144 "equal", 145 "isEqual", 146 "ne", 147 "notEq", 148 "notEqual", 149 "lt", 150 "lessThan", 151 "gt", 152 "greaterThan", 153 "int_eq", 154 "intEq", 155 "intEqual", 156 "len", 157 "length", 158 "min_len", 159 "minLen", 160 "minLength", 161 "max_len", 162 "maxLen", 163 "maxLength", 164 "email", 165 "isEmail", 166 "regex", 167 "regexp", 168 "arr", 169 "list", 170 "array", 171 "isArray", 172 "map", 173 "isMap", 174 "strings", 175 "isStrings", 176 "ints", 177 "isInts", 178 "eq_field", 179 "eqField", 180 "ne_field", 181 "neField", 182 "gte_field", 183 "gtField", 184 "gt_field", 185 "gteField", 186 "lt_field", 187 "ltField", 188 "lte_field", 189 "lteField", 190 "file", 191 "isFile", 192 "image", 193 "isImage", 194 "mime", 195 "mimeType", 196 "inMimeTypes", 197 "date", 198 "isDate", 199 "gt_date", 200 "gtDate", 201 "afterDate", 202 "lt_date", 203 "ltDate", 204 "beforeDate", 205 "gte_date", 206 "gteDate", 207 "afterOrEqualDate", 208 "lte_date", 209 "lteDate", 210 "beforeOrEqualDate", 211 "hasWhitespace", 212 "ascii", 213 "ASCII", 214 "isASCII", 215 "alpha", 216 "isAlpha", 217 "alpha_num", 218 "alphaNum", 219 "isAlphaNum", 220 "alpha_dash", 221 "alphaDash", 222 "isAlphaDash", 223 "multi_byte", 224 "multiByte", 225 "isMultiByte", 226 "base64", 227 "isBase64", 228 "dns_name", 229 "dnsName", 230 "DNSName", 231 "isDNSName", 232 "data_uri", 233 "dataURI", 234 "isDataURI", 235 "empty", 236 "isEmpty", 237 "hex_color", 238 "hexColor", 239 "isHexColor", 240 "hexadecimal", 241 "isHexadecimal", 242 "json", 243 "JSON", 244 "isJSON", 245 "lat", 246 "latitude", 247 "isLatitude", 248 "lon", 249 "longitude", 250 "isLongitude", 251 "mac", 252 "isMAC", 253 "num", 254 "number", 255 "isNumber", 256 "cn_mobile", 257 "cnMobile", 258 "isCnMobile", 259 "printableASCII", 260 "isPrintableASCII", 261 "rgbColor", 262 "RGBColor", 263 "isRGBColor", 264 "full_url", 265 "fullUrl", 266 "isFullURL", 267 "url", 268 "URL", 269 "isURL", 270 "ip", 271 "IP", 272 "isIP", 273 "ipv4", 274 "isIPv4", 275 "ipv6", 276 "isIPv6", 277 "cidr", 278 "CIDR", 279 "isCIDR", 280 "CIDRv4", 281 "isCIDRv4", 282 "CIDRv6", 283 "isCIDRv6", 284 "uuid", 285 "isUUID", 286 "uuid3", 287 "isUUID3", 288 "uuid4", 289 "isUUID4", 290 "uuid5", 291 "isUUID5", 292 "filePath", 293 "isFilePath", 294 "unixPath", 295 "isUnixPath", 296 "winPath", 297 "isWinPath", 298 "isbn10", 299 "ISBN10", 300 "isISBN10", 301 "isbn13", 302 "ISBN13", 303 "isISBN13", 304 } 305 for _, rule := range r.rules { 306 rules = append(rules, rule.Signature()) 307 } 308 309 return rules 310 }