feat(lagoon): accept string and url rules in lagoon.Validate (quick-261104-f4x)
- string requires a string value (pointers and named string types included), url a URL matching Laravel's pattern (urlPattern, shared with lagoon.ValidateRequest); both skip an empty or blank value - lagoon::validate.string and .url in the en and pl catalogs, English fallbacks for a nil translator - settings saves whose model rules use nullable|string|max:255 or nullable|url|max:500 no longer fail with an unrecognized-rule error
This commit is contained in:
@@ -51,6 +51,8 @@ func validateField(ctx context.Context, tx *gorm.DB, model any, field, rule stri
|
||||
var betweenMin, betweenMax, minArg, maxArg string
|
||||
hasNumeric := false
|
||||
hasInteger := false
|
||||
hasString := false
|
||||
hasURL := false
|
||||
for _, tok := range tokens {
|
||||
name, arg, _ := strings.Cut(tok, ":")
|
||||
switch name {
|
||||
@@ -87,6 +89,10 @@ func validateField(ctx context.Context, tx *gorm.DB, model any, field, rule stri
|
||||
}
|
||||
case "email":
|
||||
tags = append(tags, "email")
|
||||
case "string":
|
||||
hasString = true
|
||||
case "url":
|
||||
hasURL = true
|
||||
case "confirmed":
|
||||
if fmt.Sprint(val) != fmt.Sprint(values[field+"_confirmation"]) {
|
||||
return []string{validateMessage(ctx, tr, "confirmed", field, nil)}, nil
|
||||
@@ -155,6 +161,23 @@ func validateField(ctx context.Context, tx *gorm.DB, model any, field, rule stri
|
||||
if required && isEmptyValue(val) {
|
||||
return []string{validateMessage(ctx, tr, "required", field, nil)}, nil
|
||||
}
|
||||
if hasString || hasURL {
|
||||
// Like Laravel, string and url skip a blank value; required has
|
||||
// already handled emptiness.
|
||||
s, isStr := ruleString(val)
|
||||
if !isEmptyValue(val) && (!isStr || strings.TrimSpace(s) != "") {
|
||||
if hasString && !isStr {
|
||||
return []string{validateMessage(ctx, tr, "string", field, nil)}, nil
|
||||
}
|
||||
var msgs []string
|
||||
if hasURL && (!isStr || !urlPattern.MatchString(s)) {
|
||||
msgs = append(msgs, validateMessage(ctx, tr, "url", field, nil))
|
||||
}
|
||||
if len(msgs) > 0 {
|
||||
return msgs, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
usedBetween := betweenMin != "" && betweenMax != ""
|
||||
if len(tags) > 0 {
|
||||
msgs := make([]string, 0, len(tags))
|
||||
@@ -327,6 +350,26 @@ func isIntegerValue(val any) bool {
|
||||
}
|
||||
}
|
||||
|
||||
// ruleString answers the string a string or url rule checks: it
|
||||
// dereferences pointers and interfaces (a nil one is no string) and accepts
|
||||
// any value whose kind is string, named string types included.
|
||||
func ruleString(val any) (string, bool) {
|
||||
if val == nil {
|
||||
return "", false
|
||||
}
|
||||
rv := reflect.ValueOf(val)
|
||||
for rv.Kind() == reflect.Ptr || rv.Kind() == reflect.Interface {
|
||||
if rv.IsNil() {
|
||||
return "", false
|
||||
}
|
||||
rv = rv.Elem()
|
||||
}
|
||||
if rv.Kind() != reflect.String {
|
||||
return "", false
|
||||
}
|
||||
return rv.String(), true
|
||||
}
|
||||
|
||||
func numericString(val any) (string, bool) {
|
||||
if val == nil {
|
||||
return "", false
|
||||
@@ -466,6 +509,10 @@ func validateMessage(ctx context.Context, tr *phrasebook.Translator, rule, field
|
||||
return "The " + attr + " must be between " + params["min"] + " and " + params["max"] + "."
|
||||
case "boolean":
|
||||
return "The " + attr + " field must be true or false."
|
||||
case "string":
|
||||
return "The " + attr + " must be a string."
|
||||
case "url":
|
||||
return "The " + attr + " format is invalid."
|
||||
default:
|
||||
return "The " + attr + " is invalid."
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user