mirror of
https://gitee.com/johng/gf
synced 2026-07-08 14:39:50 +08:00
fix issue missing org tag value retrieving when using struct in Fields for package gdb
This commit is contained in:
@ -667,7 +667,7 @@ func (c *Core) MarshalJSON() ([]byte, error) {
|
||||
return []byte(fmt.Sprintf(`%+v`, c)), nil
|
||||
}
|
||||
|
||||
// writeSqlToLogger outputs the sql object to logger.
|
||||
// writeSqlToLogger outputs the Sql object to logger.
|
||||
// It is enabled only if configuration "debug" is true.
|
||||
func (c *Core) writeSqlToLogger(ctx context.Context, sql *Sql) {
|
||||
var transactionIdStr string
|
||||
|
||||
@ -328,6 +328,26 @@ func doQuoteString(s, charLeft, charRight string) string {
|
||||
return gstr.Join(array1, ",")
|
||||
}
|
||||
|
||||
func getFieldsFromStructOrMap(structOrMap interface{}) (fields []string) {
|
||||
fields = []string{}
|
||||
if utils.IsStruct(structOrMap) {
|
||||
structFields, _ := structs.Fields(structs.FieldsInput{
|
||||
Pointer: structOrMap,
|
||||
RecursiveOption: structs.RecursiveOptionEmbeddedNoTag,
|
||||
})
|
||||
for _, structField := range structFields {
|
||||
if tag := structField.Tag(OrmTagForStruct); tag != "" && gregex.IsMatchString(regularFieldNameRegPattern, tag) {
|
||||
fields = append(fields, tag)
|
||||
} else {
|
||||
fields = append(fields, structField.Name())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fields = gutil.Keys(structOrMap)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
@ -13,7 +13,6 @@ import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
// Fields appends `fieldNamesOrMapStruct` to the operation fields of the model, multiple fields joined using char ','.
|
||||
@ -43,7 +42,7 @@ func (m *Model) Fields(fieldNamesOrMapStruct ...interface{}) *Model {
|
||||
))
|
||||
default:
|
||||
return m.appendFieldsByStr(gstr.Join(
|
||||
m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",",
|
||||
m.mappingAndFilterToTableFields(getFieldsFromStructOrMap(r), true), ",",
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -71,7 +70,7 @@ func (m *Model) FieldsEx(fieldNamesOrMapStruct ...interface{}) *Model {
|
||||
case []string:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(r, true), ",")
|
||||
default:
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(gutil.Keys(r), true), ",")
|
||||
model.fieldsEx = gstr.Join(m.mappingAndFilterToTableFields(getFieldsFromStructOrMap(r), true), ",")
|
||||
}
|
||||
return model
|
||||
}
|
||||
|
||||
@ -33,6 +33,7 @@ var (
|
||||
db gdb.DB
|
||||
dbPrefix gdb.DB
|
||||
dbCtxStrict gdb.DB
|
||||
dbInvalid gdb.DB
|
||||
configNode gdb.ConfigNode
|
||||
)
|
||||
|
||||
@ -62,9 +63,13 @@ func init() {
|
||||
nodeCtxStrict := configNode
|
||||
nodeCtxStrict.CtxStrict = true
|
||||
|
||||
nodeInvalid := configNode
|
||||
nodeInvalid.Port = "3307"
|
||||
|
||||
gdb.AddConfigNode("test", configNode)
|
||||
gdb.AddConfigNode("prefix", nodePrefix)
|
||||
gdb.AddConfigNode("ctxstrict", nodeCtxStrict)
|
||||
gdb.AddConfigNode("nodeinvalid", nodeInvalid)
|
||||
gdb.AddConfigNode(gdb.DefaultGroupName, configNode)
|
||||
|
||||
// Default db.
|
||||
@ -109,6 +114,14 @@ func init() {
|
||||
gtest.Error(err)
|
||||
}
|
||||
dbCtxStrict.SetSchema(TestSchema1)
|
||||
|
||||
// Invalid db.
|
||||
if r, err := gdb.New("nodeinvalid"); err != nil {
|
||||
gtest.Error(err)
|
||||
} else {
|
||||
dbInvalid = r
|
||||
}
|
||||
dbInvalid.SetSchema(TestSchema1)
|
||||
}
|
||||
|
||||
func createTable(table ...string) string {
|
||||
|
||||
@ -7,10 +7,13 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/encoding/gjson"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -1086,35 +1089,53 @@ func Test_Model_Structs(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// JSON tag is only used for JSON Marshal/Unmarshal, DO NOT use it in multiple purposes!
|
||||
//func Test_Model_StructsWithJsonTag(t *testing.T) {
|
||||
// table := createInitTable()
|
||||
// defer dropTable(table)
|
||||
//
|
||||
// db.SetDebug(true)
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// type User struct {
|
||||
// Uid int `json:"id"`
|
||||
// Passport string
|
||||
// Password string
|
||||
// Name string `json:"nick_name"`
|
||||
// Time gtime.Time `json:"create_time"`
|
||||
// }
|
||||
// var users []User
|
||||
// err := db.Model(table).Order("id asc").Scan(&users)
|
||||
// if err != nil {
|
||||
// gtest.Error(err)
|
||||
// }
|
||||
// t.Assert(len(users), TableSize)
|
||||
// t.Assert(users[0].Uid, 1)
|
||||
// t.Assert(users[1].Uid, 2)
|
||||
// t.Assert(users[2].Uid, 3)
|
||||
// t.Assert(users[0].Name, "name_1")
|
||||
// t.Assert(users[1].Name, "name_2")
|
||||
// t.Assert(users[2].Name, "name_3")
|
||||
// t.Assert(users[0].Time.String(), "2018-10-24 10:00:00")
|
||||
// })
|
||||
//}
|
||||
func Test_Model_StructsWithOrmTag(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
dbInvalid.SetDebug(true)
|
||||
defer dbInvalid.SetDebug(false)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Uid int `orm:"id"`
|
||||
Passport string
|
||||
Password string `orm:"password"`
|
||||
Name string `orm:"nick_name"`
|
||||
Time gtime.Time `orm:"create_time"`
|
||||
}
|
||||
var (
|
||||
users []User
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
dbInvalid.GetLogger().SetWriter(buffer)
|
||||
defer dbInvalid.GetLogger().SetWriter(os.Stdout)
|
||||
dbInvalid.Model(table).Order("id asc").Scan(&users)
|
||||
t.Assert(
|
||||
gstr.Contains(buffer.String(), "SELECT `id`,`Passport`,`password`,`nick_name`,`create_time` FROM `user"),
|
||||
true,
|
||||
)
|
||||
})
|
||||
|
||||
//db.SetDebug(true)
|
||||
//defer db.SetDebug(false)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type A struct {
|
||||
Passport string
|
||||
Password string
|
||||
}
|
||||
type B struct {
|
||||
A
|
||||
NickName string
|
||||
}
|
||||
one, err := db.Model(table).Fields(&B{}).Where("id", 2).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(one), 3)
|
||||
t.Assert(one["nickname"], "name_2")
|
||||
t.Assert(one["passport"], "user_2")
|
||||
t.Assert(one["password"], "pass_2")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func Test_Model_Scan(t *testing.T) {
|
||||
table := createInitTable()
|
||||
|
||||
@ -75,6 +75,15 @@ const (
|
||||
RecursiveOptionEmbeddedNoTag = 2 // Recursively retrieving fields as map if the field is an embedded struct and the field has no tag.
|
||||
)
|
||||
|
||||
type FieldsInput struct {
|
||||
// Pointer should be type of struct/*struct.
|
||||
Pointer interface{}
|
||||
|
||||
// RecursiveOption specifies the way retrieving the fields recursively if the attribute
|
||||
// is an embedded struct. It is RecursiveOptionNone in default.
|
||||
RecursiveOption int
|
||||
}
|
||||
|
||||
type FieldMapInput struct {
|
||||
// Pointer should be type of struct/*struct.
|
||||
Pointer interface{}
|
||||
@ -88,6 +97,52 @@ type FieldMapInput struct {
|
||||
RecursiveOption int
|
||||
}
|
||||
|
||||
// Fields retrieves and returns the fields of `pointer` as slice.
|
||||
func Fields(in FieldsInput) ([]*Field, error) {
|
||||
var (
|
||||
fieldFilterMap = make(map[string]struct{})
|
||||
retrievedFields = make([]*Field, 0)
|
||||
)
|
||||
rangeFields, err := getFieldValues(in.Pointer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := 0; i < len(rangeFields); i++ {
|
||||
field := rangeFields[i]
|
||||
if _, ok := fieldFilterMap[field.Name()]; ok {
|
||||
continue
|
||||
}
|
||||
// Only retrieve exported attributes.
|
||||
if !field.IsExported() {
|
||||
continue
|
||||
}
|
||||
if field.IsEmbedded() {
|
||||
if in.RecursiveOption != RecursiveOptionNone {
|
||||
switch in.RecursiveOption {
|
||||
case RecursiveOptionEmbeddedNoTag:
|
||||
if field.TagStr() != "" {
|
||||
break
|
||||
}
|
||||
fallthrough
|
||||
case RecursiveOptionEmbedded:
|
||||
structFields, err := Fields(FieldsInput{
|
||||
Pointer: field.Value,
|
||||
RecursiveOption: in.RecursiveOption,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rangeFields = append(rangeFields, structFields...)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
fieldFilterMap[field.Name()] = struct{}{}
|
||||
retrievedFields = append(retrievedFields, field)
|
||||
}
|
||||
return retrievedFields, nil
|
||||
}
|
||||
|
||||
// FieldMap retrieves and returns struct field as map[name/tag]*Field from `pointer`.
|
||||
//
|
||||
// The parameter `pointer` should be type of struct/*struct.
|
||||
@ -99,8 +154,8 @@ type FieldMapInput struct {
|
||||
// is an embedded struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
func FieldMap(input FieldMapInput) (map[string]*Field, error) {
|
||||
fields, err := getFieldValues(input.Pointer)
|
||||
func FieldMap(in FieldMapInput) (map[string]*Field, error) {
|
||||
fields, err := getFieldValues(in.Pointer)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -114,7 +169,7 @@ func FieldMap(input FieldMapInput) (map[string]*Field, error) {
|
||||
continue
|
||||
}
|
||||
tagValue = ""
|
||||
for _, p := range input.PriorityTagArray {
|
||||
for _, p := range in.PriorityTagArray {
|
||||
tagValue = field.Tag(p)
|
||||
if tagValue != "" && tagValue != "-" {
|
||||
break
|
||||
@ -125,8 +180,8 @@ func FieldMap(input FieldMapInput) (map[string]*Field, error) {
|
||||
if tagValue != "" {
|
||||
mapField[tagValue] = tempField
|
||||
} else {
|
||||
if input.RecursiveOption != RecursiveOptionNone && field.IsEmbedded() {
|
||||
switch input.RecursiveOption {
|
||||
if in.RecursiveOption != RecursiveOptionNone && field.IsEmbedded() {
|
||||
switch in.RecursiveOption {
|
||||
case RecursiveOptionEmbeddedNoTag:
|
||||
if field.TagStr() != "" {
|
||||
mapField[field.Name()] = tempField
|
||||
@ -136,8 +191,8 @@ func FieldMap(input FieldMapInput) (map[string]*Field, error) {
|
||||
case RecursiveOptionEmbedded:
|
||||
m, err := FieldMap(FieldMapInput{
|
||||
Pointer: field.Value,
|
||||
PriorityTagArray: input.PriorityTagArray,
|
||||
RecursiveOption: input.RecursiveOption,
|
||||
PriorityTagArray: in.PriorityTagArray,
|
||||
RecursiveOption: in.RecursiveOption,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
||||
@ -13,6 +13,7 @@ import (
|
||||
)
|
||||
|
||||
// ParseTag parses tag string into map.
|
||||
// For example, tag ParseTag(`v:"required" p:"id" d:"1"`) => map[v:required p:id d:1].
|
||||
func ParseTag(tag string) map[string]string {
|
||||
var (
|
||||
key string
|
||||
@ -68,7 +69,9 @@ func ParseTag(tag string) map[string]string {
|
||||
//
|
||||
// The parameter `pointer` should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
// Note that,
|
||||
// 1. It only retrieves the exported attributes with first letter up-case from struct.
|
||||
// 2. The parameter `priority` should be given, it only retrieves fields that has given tag.
|
||||
func TagFields(pointer interface{}, priority []string) ([]*Field, error) {
|
||||
return getFieldValuesByTagPriority(pointer, priority, map[string]struct{}{})
|
||||
}
|
||||
@ -77,7 +80,9 @@ func TagFields(pointer interface{}, priority []string) ([]*Field, error) {
|
||||
//
|
||||
// The parameter `pointer` should be type of struct/*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
// Note that,
|
||||
// 1. It only retrieves the exported attributes with first letter up-case from struct.
|
||||
// 2. The parameter `priority` should be given, it only retrieves fields that has given tag.
|
||||
func TagMapName(pointer interface{}, priority []string) (map[string]string, error) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
if err != nil {
|
||||
@ -93,7 +98,9 @@ func TagMapName(pointer interface{}, priority []string) (map[string]string, erro
|
||||
// TagMapField retrieves struct tags as map[tag]*Field from `pointer`, and returns it.
|
||||
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
|
||||
//
|
||||
// Note that it only retrieves the exported attributes with first letter up-case from struct.
|
||||
// Note that,
|
||||
// 1. It only retrieves the exported attributes with first letter up-case from struct.
|
||||
// 2. The parameter `priority` should be given, it only retrieves fields that has given tag.
|
||||
func TagMapField(object interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(object, priority)
|
||||
if err != nil {
|
||||
|
||||
@ -102,6 +102,26 @@ func Test_StructOfNilPointer(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Fields(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Id int
|
||||
Name string `params:"name"`
|
||||
Pass string `my-tag1:"pass1" my-tag2:"pass2" params:"pass"`
|
||||
}
|
||||
var user *User
|
||||
fields, _ := structs.Fields(user)
|
||||
t.Assert(len(fields), 3)
|
||||
t.Assert(fields[0].Name(), "Id")
|
||||
t.Assert(fields[1].Name(), "Name")
|
||||
t.Assert(fields[1].Tag("params"), "name")
|
||||
t.Assert(fields[2].Name(), "Pass")
|
||||
t.Assert(fields[2].Tag("my-tag1"), "pass1")
|
||||
t.Assert(fields[2].Tag("my-tag2"), "pass2")
|
||||
t.Assert(fields[2].Tag("params"), "pass")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_FieldMap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
|
||||
Reference in New Issue
Block a user