Is it possible to validate that on a given struct at least one of two options is required? I often have a struct where as long as one out of 2 of the fields are filled in, it will pass validation. One basic example would be having a username field and an email field (just a basic example though).
Currently there are no built in tags to do Cross Field OR validation. once getting into situations like you defined above, I consider them to be complex validation rules that even if built in tags could be used, it would become very hard to read.
But the library has a an easy reusable way to achieve what you want by creating a custom validation function, and then you may use and reuse it as a tag in your structs.
// define funciton
func oneFieldSet(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
// use topStruct instead of currentStructOrField below if want function to be able to do cross struct cross field validation
currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)
if !ok || currentKind != fieldKind {
return false
}
// if field has a value, just like the hasValue function then return true
// if currentField has a value return true
// return false by default
}
validate := //new validator
validate.RegisterValidation("eitherrequired", oneFieldSet)
type User struct {
Username string `validate:"eitherrequired=Email"`
Email string `validate:"eitherrequired=Username"`
}
then you could reuse this tag/validation function in a bunch of your structs
Does that make sense?
P.S. you gave me a great idea, that I should expose the existing validators so that you can use them inside custom ones; then you could use hasValue ( which is the function that "required" tag runs ) in your custom function.
Hey @scisci
I've updated validator to expose the baked in functions for use within custom functions, so below is a more complete example using the exposed functions.
// define funciton
func oneFieldSet(v *Validate, topStruct reflect.Value, currentStructOrField reflect.Value, field reflect.Value, fieldType reflect.Type, fieldKind reflect.Kind, param string) bool {
// use topStruct instead of currentStructOrField below if want function to be able to do cross struct cross field validation
currentField, currentKind, ok := v.GetStructFieldOK(currentStructOrField, param)
if !ok || currentKind != fieldKind {
return false
}
if validator.HasValue(v, topStruct, currentStructOrField, field, fieldType, fieldKind, param) {
return true
}
if validator.HasValue(v, topStruct, currentStructOrField, currentField, fieldType, currentKind, param) {
return true
}
return false
}
validate := //new validator
validate.RegisterValidation("eitherrequired", oneFieldSet)
type User struct {
Username string `validate:"eitherrequired=Email"`
Email string `validate:"eitherrequired=Username"`
}
will this work for your needs? it should be generic enough to use within many of your structs
awesome! yeah that totally works for me. I will look into implementing it as soon as possible. thanks @joeybloggs I had some more complex validation as well so using a validation function makes total sense.
perfect! glad I could help @scisci
if you need any help or have any question, just let me know.
Hey @scisci thought I'd let you know about Struct Level Validations see here: https://github.com/go-playground/validator/releases/tag/v8.7
this may be an even better way to do your username or email check :smile:
Most helpful comment
Hey @scisci thought I'd let you know about Struct Level Validations see here: https://github.com/go-playground/validator/releases/tag/v8.7
this may be an even better way to do your username or email check :smile: