Skip to content

Commit

Permalink
feat: optimize validation (#594)
Browse files Browse the repository at this point in the history
* feat: optimize validation

* update description

* remove debug
  • Loading branch information
hwbrzzl authored Aug 12, 2024
1 parent 2411e07 commit 1fb6d9a
Show file tree
Hide file tree
Showing 5 changed files with 310 additions and 97 deletions.
2 changes: 1 addition & 1 deletion console/cli_context.go
Original file line number Diff line number Diff line change
Expand Up @@ -230,7 +230,7 @@ func (r *CliContext) Secret(question string, option ...console.SecretOption) (st
}

func (r *CliContext) Spinner(message string, option console.SpinnerOption) error {
style := lipgloss.NewStyle().Foreground(lipgloss.Color("#8ED3F9"))
style := lipgloss.NewStyle().Foreground(lipgloss.CompleteColor{TrueColor: "#3D8C8D", ANSI256: "30", ANSI: "6"})
spin := spinner.New().Title(message).Style(style).TitleStyle(style)

var err error
Expand Down
52 changes: 52 additions & 0 deletions support/maps/maps.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
package maps

import (
"reflect"
)

// Add an element to a map if it doesn't exist.
func Add[K comparable, V any](mp map[K]V, k K, v V) {
if Exists(mp, k) {
Expand All @@ -25,6 +29,54 @@ func Forget[K comparable, V any](mp map[K]V, keys ...K) {
}
}

func FromStruct(data any) map[string]any {
res := make(map[string]any)
dataType := reflect.TypeOf(data)
dataValue := reflect.ValueOf(data)

if dataType.Kind() == reflect.Pointer {
dataType = dataType.Elem()
dataValue = dataValue.Elem()
}

if dataType.Kind() != reflect.Struct {
return res
}

for i := 0; i < dataType.NumField(); i++ {
fieldType := dataType.Field(i)
fieldValue := dataValue.Field(i)

if !fieldType.IsExported() {
continue
}

if fieldValue.Kind() == reflect.Pointer {
if fieldValue.IsNil() {
res[fieldType.Name] = nil
continue
}

fieldValue = fieldValue.Elem()
}

if fieldValue.Kind() == reflect.Struct {
subStructMap := FromStruct(fieldValue.Interface())
if fieldType.Anonymous {
for key, value := range subStructMap {
res[key] = value
}
} else {
res[fieldType.Name] = subStructMap
}
} else {
res[fieldType.Name] = fieldValue.Interface()
}
}

return res
}

// Get an element from a map
func Get[K comparable, V any](mp map[K]V, key K, defaults ...V) V {
val, ok := mp[key]
Expand Down
38 changes: 38 additions & 0 deletions support/maps/maps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,44 @@ func TestForget(t *testing.T) {
}, gMp)
}

func TestFromStruct(t *testing.T) {
type One struct {
Name string
Age int
}
type Two struct {
Height int
}
type Three struct {
Two
One One
Name string
age int
}
data := Three{
Name: "Three",
Two: Two{
Height: 1,
},
One: One{
Name: "One",
Age: 18,
},
age: 1,
}

res := FromStruct(data)

assert.Equal(t, "Three", res["Name"])
assert.Equal(t, 1, res["Height"])

one, ok := res["One"].(map[string]any)

assert.True(t, ok)
assert.Equal(t, "One", one["Name"])
assert.Equal(t, 18, one["Age"])
}

func TestGet(t *testing.T) {
mp := map[string]any{
"name": "Krishan",
Expand Down
50 changes: 32 additions & 18 deletions validation/validator.go
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
package validation

import (
"net/url"
"reflect"
"strings"

"github.com/gookit/validate"
"github.com/mitchellh/mapstructure"
"github.com/spf13/cast"

httpvalidate "github.com/goravel/framework/contracts/validation"
"github.com/goravel/framework/support/carbon"
"github.com/goravel/framework/support/str"
"github.com/goravel/framework/support/maps"
)

func init() {
Expand Down Expand Up @@ -38,21 +38,39 @@ func (v *Validator) Bind(ptr any) error {
return nil
}

// SafeData only contains the data that is defined in the rules,
// we want user can the original data that is not defined in the rules,
// so that user doesn't need to define rules for all fields.
data := v.instance.SafeData()

// When rules check slice: "field.*", SafeData will only have the "field.*" key, doesn't have the field key.
for key, value := range data {
if str.Of(key).EndsWith(".*") {
realKey := strings.ReplaceAll(key, ".*", "")
if _, exist := data[realKey]; !exist {
data[realKey] = value
if formData, ok := v.data.(*validate.FormData); ok {
if values, ok := v.data.Src().(url.Values); ok {
for key, value := range values {
if _, exist := data[key]; !exist {
data[key] = value[0]
}
}
}
}

if formData, ok := v.data.(*validate.FormData); ok {
for key, value := range formData.Files {
data[key] = value
for key, value := range formData.Files {
if _, exist := data[key]; !exist {
data[key] = value
}
}
}
} else if _, ok := v.data.(*validate.MapData); ok {
values := v.data.Src().(map[string]any)
for key, value := range values {
if _, exist := data[key]; !exist {
data[key] = value
}
}
} else {
if srcMap := maps.FromStruct(v.data.Src()); len(srcMap) > 0 {
for key, value := range srcMap {
if _, exist := data[key]; !exist {
data[key] = value
}
}
}
}

Expand All @@ -65,11 +83,7 @@ func (v *Validator) Bind(ptr any) error {
return err
}

if err = decoder.Decode(data); err != nil {
return err
}

return nil
return decoder.Decode(data)
}

func (v *Validator) Errors() httpvalidate.Errors {
Expand Down
Loading

0 comments on commit 1fb6d9a

Please sign in to comment.