The validation component supports powerful recursive validation (nested validation) features. If the properties or key values in the given validation data are of type struct/map/slice
, recursive validation will be automatically performed. Let's look at a few examples:
Example 1, Recursive Validation: struct
package main
import (
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
type SearchReq struct {
Key string `v:"required"`
Option SearchOption
}
type SearchOption struct {
Page int `v:"min:1"`
Size int `v:"max:100"`
}
func main() {
var (
ctx = gctx.New()
req = SearchReq{
Key: "GoFrame",
Option: SearchOption{
Page: 1,
Size: 10000,
},
}
)
err := g.Validator().Data(req).Run(ctx)
fmt.Println(err)
}
After execution, the terminal outputs:
The Size value `10000` must be equal or lesser than 100
Example 2, Recursive Validation: slice
package main
import (
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
type Student struct {
Name string `v:"required#Student Name is required"`
Age int
}
type Teacher struct {
Name string
Students []Student
}
var (
ctx = gctx.New()
teacher = Teacher{}
data = g.Map{
"name": "john",
"students": `[{"age":2},{"name":"jack", "age":4}]`,
}
)
err := g.Validator().Assoc(data).Data(teacher).Run(ctx)
fmt.Println(err)
}
After execution, the terminal outputs:
Student Name is required
Example 3, Recursive Validation: map
package main
import (
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
type Student struct {
Name string `v:"required#Student Name is required"`
Age int
}
type Teacher struct {
Name string
Students map[string]Student
}
var (
ctx = gctx.New()
teacher = Teacher{
Name: "Smith",
Students: map[string]Student{
"john": {Name: "", Age: 18},
},
}
)
err := g.Validator().Data(teacher).Run(ctx)
fmt.Println(err)
}
After execution, the terminal outputs:
Student Name is required
Note: Impact of Empty Objects on Recursive Validation
package main
import (
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
type Student struct {
Name string `v:"required"`
}
type Teacher struct {
Students Student
}
var (
ctx = gctx.New()
teacher = Teacher{}
data = g.Map{
"students": nil,
}
)
err := g.Validator().Assoc(data).Data(teacher).Run(ctx)
fmt.Println(err)
}
After execution, the terminal outputs:
Student Name is required
Some may find it strange why the Name
field of the Student
struct is validated even though the Student
field value was not passed. This is because the Student
property is an empty struct with default values (the Name
default value is an empty string). In recursive validation, although Student
is not a required parameter, meaning you could choose not to pass it, if it is passed, it will be validated according to the validation rules of the properties within it (an empty object with default values is also considered to have a value). You can compare this with the differences in the code below:
package main
import (
"fmt"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gctx"
)
func main() {
type Student struct {
Name string `v:"required"`
}
type Teacher struct {
Students *Student
}
var (
ctx = gctx.New()
teacher = Teacher{}
data = g.Map{
"students": nil,
}
)
err := g.Validator().Assoc(data).Data(teacher).Run(ctx)
fmt.Println(err)
}
The only difference from the previous example is that the Student
property has been changed from a struct to a struct pointer *Student
, thus the property is no longer an empty object with default values. After execution, the terminal outputs nothing, indicating validation passed.