improve Struct convertion for package gdb; improve IsNil function for package internal/empty

This commit is contained in:
John Guo 2021-01-09 21:05:47 +08:00
parent 8365ce9d29
commit 92b791eb08
7 changed files with 74 additions and 21 deletions

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -48,9 +48,9 @@ type apiMapStrAny interface {
}
const (
ORM_TAG_FOR_STRUCT = "orm"
ORM_TAG_FOR_UNIQUE = "unique"
ORM_TAG_FOR_PRIMARY = "primary"
OrmTagForStruct = "orm"
OrmTagForUnique = "unique"
OrmTagForPrimary = "primary"
)
var (
@ -58,7 +58,7 @@ var (
quoteWordReg = regexp.MustCompile(`^[a-zA-Z0-9\-_]+$`)
// Priority tags for struct converting for orm field mapping.
structTagPriority = append([]string{ORM_TAG_FOR_STRUCT}, gconv.StructTagPriority...)
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
)
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
@ -315,14 +315,14 @@ func doQuoteString(s, charLeft, charRight string) string {
// GetWhereConditionOfStruct returns the where condition sql and arguments by given struct pointer.
// This function automatically retrieves primary or unique field and its attribute value as condition.
func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}, err error) {
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
if err != nil {
return "", nil, err
}
array := ([]string)(nil)
for _, field := range tagField {
array = strings.Split(field.TagValue, ",")
if len(array) > 1 && gstr.InArray([]string{ORM_TAG_FOR_UNIQUE, ORM_TAG_FOR_PRIMARY}, array[1]) {
if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) {
return array[0], []interface{}{field.Value()}, nil
}
if len(where) > 0 {
@ -336,14 +336,14 @@ func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interf
// GetPrimaryKey retrieves and returns primary key field name from given struct.
func GetPrimaryKey(pointer interface{}) (string, error) {
tagField, err := structs.TagFields(pointer, []string{ORM_TAG_FOR_STRUCT})
tagField, err := structs.TagFields(pointer, []string{OrmTagForStruct})
if err != nil {
return "", err
}
array := ([]string)(nil)
for _, field := range tagField {
array = strings.Split(field.TagValue, ",")
if len(array) > 1 && array[1] == ORM_TAG_FOR_PRIMARY {
if len(array) > 1 && array[1] == OrmTagForPrimary {
return array[0], nil
}
}
@ -741,7 +741,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
// convertMapToStruct maps the <data> to given struct.
// Note that the given parameter <pointer> should be a pointer to s struct.
func convertMapToStruct(data map[string]interface{}, pointer interface{}) error {
tagNameMap, err := structs.TagMapName(pointer, []string{ORM_TAG_FOR_STRUCT})
tagNameMap, err := structs.TagMapName(pointer, []string{OrmTagForStruct})
if err != nil {
return err
}

View File

@ -11,6 +11,7 @@ import (
"github.com/gogf/gf/container/gmap"
"github.com/gogf/gf/encoding/gparser"
"github.com/gogf/gf/errors/gerror"
"github.com/gogf/gf/internal/empty"
"github.com/gogf/gf/util/gconv"
"reflect"
)
@ -47,7 +48,7 @@ func (r Record) GMap() *gmap.StrAnyMap {
// Note that it returns sql.ErrNoRows if <r> is empty.
func (r Record) Struct(pointer interface{}) error {
// If the record is empty, it returns error.
if r.IsEmpty() {
if r.IsEmpty() && !empty.IsNil(pointer, true) {
return sql.ErrNoRows
}
// Special handling for parameter type: reflect.Value

View File

@ -981,6 +981,18 @@ func Test_Model_Struct(t *testing.T) {
err := db.Table(table).Where("id=-1").Struct(user)
t.Assert(err, sql.ErrNoRows)
})
gtest.C(t, func(t *gtest.T) {
type User struct {
Id int
Passport string
Password string
NickName string
CreateTime *gtime.Time
}
var user *User
err := db.Table(table).Where("id=-1").Struct(&user)
t.Assert(err, nil)
})
}
func Test_Model_Struct_CustomType(t *testing.T) {

View File

@ -1,4 +1,4 @@
// Copyright GoFrame Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -262,7 +262,8 @@ func Test_Struct_Empty(t *testing.T) {
one, err := db.Table(table).Where("id=100").One()
t.Assert(err, nil)
var user *User
t.AssertNE(one.Struct(&user), nil)
t.Assert(one.Struct(&user), nil)
t.Assert(user, nil)
})
gtest.C(t, func(t *gtest.T) {

View File

@ -52,9 +52,12 @@ func TryCatch(try func(), catch ...func(exception error)) {
}
// IsNil checks whether given <value> is nil.
// Parameter <traceSource> is used for tracing to the source variable if given <value> is type
// of a pinter that also points to a pointer. It returns nil if the source is nil when <traceSource>
// is true.
// Note that it might use reflect feature which affects performance a little bit.
func IsNil(value interface{}) bool {
return empty.IsNil(value)
func IsNil(value interface{}, traceSource ...bool) bool {
return empty.IsNil(value, traceSource...)
}
// IsEmpty checks whether given <value> empty.

View File

@ -1,10 +1,10 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame gf Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// Package empty provides functions for checking empty variables.
// Package empty provides functions for checking empty/nil variables.
package empty
import (
@ -152,8 +152,11 @@ func IsEmpty(value interface{}) bool {
}
// IsNil checks whether given <value> is nil.
// Parameter <traceSource> is used for tracing to the source variable if given <value> is type
// of a pinter that also points to a pointer. It returns nil if the source is nil when <traceSource>
// is true.
// Note that it might use reflect feature which affects performance a little bit.
func IsNil(value interface{}) bool {
func IsNil(value interface{}, traceSource ...bool) bool {
if value == nil {
return true
}
@ -168,10 +171,24 @@ func IsNil(value interface{}) bool {
reflect.Map,
reflect.Slice,
reflect.Func,
reflect.Ptr,
reflect.Interface,
reflect.UnsafePointer:
return rv.IsNil()
return !rv.IsValid() || rv.IsNil()
case reflect.Ptr:
if len(traceSource) > 0 && traceSource[0] {
for rv.Kind() == reflect.Ptr {
rv = rv.Elem()
}
if !rv.IsValid() {
return true
}
if rv.Kind() == reflect.Ptr {
return rv.IsNil()
}
} else {
return !rv.IsValid() || rv.IsNil()
}
}
return false
}

View File

@ -1,4 +1,4 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
// Copyright GoFrame Author(https://goframe.org). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
@ -108,3 +108,22 @@ func TestIsEmpty(t *testing.T) {
t.Assert(empty.IsEmpty(tmpF6), false)
})
}
func TestIsNil(t *testing.T) {
gtest.C(t, func(t *gtest.T) {
t.Assert(empty.IsNil(nil), true)
})
gtest.C(t, func(t *gtest.T) {
var i int
t.Assert(empty.IsNil(i), false)
})
gtest.C(t, func(t *gtest.T) {
var i *int
t.Assert(empty.IsNil(i), true)
})
gtest.C(t, func(t *gtest.T) {
var i *int
t.Assert(empty.IsNil(&i), false)
t.Assert(empty.IsNil(&i, true), true)
})
}