Skip to main content
Version: 2.8.x(Latest)

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.