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  }