mirror of
https://gitee.com/johng/gf
synced 2026-06-06 02:25:47 +08:00
great! completed 'with' feature for package gdb
This commit is contained in:
@ -783,25 +783,3 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
})
|
||||
return newQuery
|
||||
}
|
||||
|
||||
// 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{OrmTagForStruct})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// It retrieves and returns the mapping between orm tag and the struct attribute name.
|
||||
var (
|
||||
mapping = make(map[string]string)
|
||||
tagFieldName string
|
||||
)
|
||||
for tag, attr := range tagNameMap {
|
||||
tagFieldName = strings.Split(tag, ",")[0]
|
||||
if !gregex.IsMatchString(regularFieldNameRegPattern, tagFieldName) {
|
||||
continue
|
||||
}
|
||||
mapping[tagFieldName] = attr
|
||||
}
|
||||
return gconv.Struct(data, pointer, mapping)
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ type Model struct {
|
||||
fields string // Operation fields, multiple fields joined using char ','.
|
||||
fieldsEx string // Excluded operation fields, multiple fields joined using char ','.
|
||||
withArray []interface{} // Arguments for With feature.
|
||||
withAll bool // Enable model association operations on all objects that have "with" tag in the struct.
|
||||
extraArgs []interface{} // Extra custom arguments for sql.
|
||||
whereHolder []*whereHolder // Condition strings for where operation.
|
||||
groupBy string // Used for "group by" statement.
|
||||
|
||||
@ -228,7 +228,7 @@ func (m *Model) Struct(pointer interface{}, where ...interface{}) error {
|
||||
if err = one.Struct(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.doWithScan(pointer)
|
||||
return m.doWithScanStruct(pointer)
|
||||
}
|
||||
|
||||
// Structs retrieves records from table and converts them into given struct slice.
|
||||
@ -252,7 +252,10 @@ func (m *Model) Structs(pointer interface{}, where ...interface{}) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.Structs(pointer)
|
||||
if err = all.Structs(pointer); err != nil {
|
||||
return err
|
||||
}
|
||||
return m.doWithScanStructs(pointer)
|
||||
}
|
||||
|
||||
// Scan automatically calls Struct or Structs function according to the type of parameter `pointer`.
|
||||
|
||||
@ -13,27 +13,89 @@ import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/text/gregex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
func (m *Model) With(structAttrPointer interface{}) *Model {
|
||||
// With creates and returns an ORM model based on meta data of given object.
|
||||
// It also enables model association operations feature on given `object`.
|
||||
// It can be called multiple times to add one or more objects to model and enable
|
||||
// their mode association operations feature.
|
||||
// For example, if given struct definition:
|
||||
// type User struct {
|
||||
// gmeta.Meta `orm:"table:user"`
|
||||
// Id int `json:"id"`
|
||||
// Name string `json:"name"`
|
||||
// UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
// UserScores []*UserScores `orm:"with:uid=id"`
|
||||
// }
|
||||
// We can enable model association operations on attribute `UserDetail` and `UserScores` by:
|
||||
// db.With(User{}.UserDetail).With(User{}.UserDetail).Scan(xxx)
|
||||
// Or:
|
||||
// db.With(UserDetail{}).With(UserDetail{}).Scan(xxx)
|
||||
func (m *Model) With(object interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if m.tables == "" {
|
||||
m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(structAttrPointer))
|
||||
m.tables = m.db.QuotePrefixTableName(getTableNameFromOrmTag(object))
|
||||
return model
|
||||
}
|
||||
model.withArray = append(model.withArray, structAttrPointer)
|
||||
model.withArray = append(model.withArray, object)
|
||||
return model
|
||||
}
|
||||
|
||||
func (m *Model) doWithScan(pointer interface{}) error {
|
||||
if len(m.withArray) == 0 {
|
||||
// WithAll enables model association operations on all objects that have "with" tag in the struct.
|
||||
func (m *Model) WithAll() *Model {
|
||||
model := m.getModel()
|
||||
model.withAll = true
|
||||
return model
|
||||
}
|
||||
|
||||
// getWithTagObjectArrayFrom retrieves and returns object array that have "with" tag in the struct.
|
||||
func (m *Model) getWithTagObjectArrayFrom(pointer interface{}) ([]interface{}, error) {
|
||||
fieldMap, err := structs.FieldMap(pointer, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
withTagObjectArray := make([]interface{}, 0)
|
||||
for _, fieldValue := range fieldMap {
|
||||
var (
|
||||
withTag string
|
||||
ormTag = fieldValue.Tag(OrmTagForStruct)
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
withTag = match[1]
|
||||
}
|
||||
if withTag == "" {
|
||||
continue
|
||||
}
|
||||
withTagObjectArray = append(withTagObjectArray, fieldValue.Value.Interface())
|
||||
}
|
||||
return withTagObjectArray, nil
|
||||
}
|
||||
|
||||
// doWithScanStruct handles model association operations feature for single struct.
|
||||
func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
var (
|
||||
err error
|
||||
withArray = m.withArray
|
||||
)
|
||||
if m.withAll {
|
||||
withArray, err = m.getWithTagObjectArrayFrom(pointer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(withArray) == 0 {
|
||||
return nil
|
||||
}
|
||||
fieldMap, err := structs.FieldMap(pointer, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, withItem := range m.withArray {
|
||||
for withIndex, withItem := range withArray {
|
||||
withItemReflectValueType, err := structs.StructType(withItem)
|
||||
if err != nil {
|
||||
return err
|
||||
@ -81,10 +143,109 @@ func (m *Model) doWithScan(pointer interface{}) error {
|
||||
relatedAttrName, withTag,
|
||||
)
|
||||
}
|
||||
err = m.db.With(fieldValue.Value).
|
||||
Fields(withItemReflectValueType.FieldKeys()).
|
||||
bindToReflectValue := fieldValue.Value
|
||||
switch bindToReflectValue.Kind() {
|
||||
case reflect.Array, reflect.Slice:
|
||||
if bindToReflectValue.CanAddr() {
|
||||
bindToReflectValue = bindToReflectValue.Addr()
|
||||
}
|
||||
}
|
||||
model := m.db.With(fieldValue.Value)
|
||||
for i, v := range withArray {
|
||||
if i == withIndex {
|
||||
continue
|
||||
}
|
||||
model = model.With(v)
|
||||
}
|
||||
err = model.Fields(withItemReflectValueType.FieldKeys()).
|
||||
Where(relatedFieldName, relatedFieldValue).
|
||||
Scan(fieldValue.Value)
|
||||
Scan(bindToReflectValue)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// doWithScanStructs handles model association operations feature for struct slice.
|
||||
func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
var (
|
||||
err error
|
||||
withArray = m.withArray
|
||||
)
|
||||
if m.withAll {
|
||||
withArray, err = m.getWithTagObjectArrayFrom(pointer)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if len(withArray) == 0 {
|
||||
return nil
|
||||
}
|
||||
fieldMap, err := structs.FieldMap(pointer, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for withIndex, withItem := range withArray {
|
||||
withItemReflectValueType, err := structs.StructType(withItem)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
withItemReflectValueTypeStr := gstr.TrimAll(withItemReflectValueType.String(), "*[]")
|
||||
for fieldName, fieldValue := range fieldMap {
|
||||
var (
|
||||
fieldType = fieldValue.Type()
|
||||
fieldTypeStr = gstr.TrimAll(fieldType.String(), "*[]")
|
||||
)
|
||||
if gstr.Compare(fieldTypeStr, withItemReflectValueTypeStr) == 0 {
|
||||
var (
|
||||
withTag string
|
||||
ormTag = fieldValue.Tag(OrmTagForStruct)
|
||||
match, _ = gregex.MatchString(
|
||||
fmt.Sprintf(`%s\s*:\s*([^,]+)`, OrmTagForWith),
|
||||
ormTag,
|
||||
)
|
||||
)
|
||||
if len(match) > 1 {
|
||||
withTag = match[1]
|
||||
}
|
||||
if withTag == "" {
|
||||
continue
|
||||
}
|
||||
array := gstr.SplitAndTrim(withTag, "=")
|
||||
if len(array) != 2 {
|
||||
return gerror.Newf(`invalid with tag "%s"`, withTag)
|
||||
}
|
||||
var (
|
||||
relatedFieldName = array[0]
|
||||
relatedAttrName = array[1]
|
||||
relatedFieldValue interface{}
|
||||
)
|
||||
// Find the value slice of related attribute from `pointer`.
|
||||
for attributeName, _ := range fieldMap {
|
||||
if utils.EqualFoldWithoutChars(attributeName, relatedAttrName) {
|
||||
relatedFieldValue = ListItemValuesUnique(pointer, attributeName)
|
||||
break
|
||||
}
|
||||
}
|
||||
if relatedFieldValue == nil {
|
||||
return gerror.Newf(
|
||||
`cannot find the related value for attribute name "%s" of with tag "%s"`,
|
||||
relatedAttrName, withTag,
|
||||
)
|
||||
}
|
||||
model := m.db.With(fieldValue.Value)
|
||||
for i, v := range withArray {
|
||||
if i == withIndex {
|
||||
continue
|
||||
}
|
||||
model = model.With(v)
|
||||
}
|
||||
err = model.Fields(withItemReflectValueType.FieldKeys()).
|
||||
Where(relatedFieldName, relatedFieldValue).
|
||||
ScanList(pointer, fieldName, withTag)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@ -10,10 +10,8 @@ import (
|
||||
"database/sql"
|
||||
"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"
|
||||
)
|
||||
|
||||
// Json converts `r` to JSON format content.
|
||||
@ -54,26 +52,7 @@ func (r Record) Struct(pointer interface{}) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// Special handling for parameter type: reflect.Value
|
||||
if _, ok := pointer.(reflect.Value); ok {
|
||||
return convertMapToStruct(r.Map(), pointer)
|
||||
}
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(pointer)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if reflectKind != reflect.Ptr {
|
||||
return gerror.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind == reflect.Invalid {
|
||||
return gerror.New("parameter is an invalid pointer, maybe nil")
|
||||
}
|
||||
if reflectKind != reflect.Ptr && reflectKind != reflect.Struct {
|
||||
return gerror.New("parameter should be type of *struct/**struct")
|
||||
}
|
||||
return convertMapToStruct(r.Map(), pointer)
|
||||
return gconv.StructTag(r.Map(), pointer, OrmTagForStruct)
|
||||
}
|
||||
|
||||
// IsEmpty checks and returns whether `r` is empty.
|
||||
|
||||
@ -7,13 +7,10 @@
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"math"
|
||||
"reflect"
|
||||
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"math"
|
||||
)
|
||||
|
||||
// IsEmpty checks and returns whether `r` is empty.
|
||||
@ -192,49 +189,5 @@ func (r Result) RecordKeyUint(key string) map[uint]Record {
|
||||
// Structs converts `r` to struct slice.
|
||||
// Note that the parameter `pointer` should be type of *[]struct/*[]*struct.
|
||||
func (r Result) Structs(pointer interface{}) (err error) {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(pointer)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
if reflectKind != reflect.Ptr {
|
||||
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
||||
return fmt.Errorf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
length := len(r)
|
||||
if length == 0 {
|
||||
// The pointed slice is not empty.
|
||||
if reflectValue.Len() > 0 {
|
||||
// It here checks if it has struct item, which is already initialized.
|
||||
// It then returns error to warn the developer its empty and no conversion.
|
||||
if v := reflectValue.Index(0); v.Kind() != reflect.Ptr {
|
||||
return sql.ErrNoRows
|
||||
}
|
||||
}
|
||||
// Do nothing for empty struct slice.
|
||||
return nil
|
||||
}
|
||||
var (
|
||||
reflectType = reflect.TypeOf(pointer)
|
||||
array = reflect.MakeSlice(reflectType.Elem(), length, length)
|
||||
itemType = array.Index(0).Type()
|
||||
itemKind = itemType.Kind()
|
||||
)
|
||||
for i := 0; i < length; i++ {
|
||||
var elem reflect.Value
|
||||
if itemKind == reflect.Ptr {
|
||||
elem = reflect.New(itemType.Elem())
|
||||
} else {
|
||||
elem = reflect.New(itemType).Elem()
|
||||
}
|
||||
if err = r[i].Struct(elem); err != nil {
|
||||
return fmt.Errorf(`slice element conversion failed: %s`, err.Error())
|
||||
}
|
||||
array.Index(i).Set(elem)
|
||||
}
|
||||
reflect.ValueOf(pointer).Elem().Set(array)
|
||||
return nil
|
||||
return gconv.StructsTag(r, pointer, OrmTagForStruct)
|
||||
}
|
||||
|
||||
@ -14,7 +14,7 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Table_Relation_With(t *testing.T) {
|
||||
func Test_Table_Relation_With_Scan(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
@ -26,7 +26,7 @@ id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
@ -37,7 +37,7 @@ uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
@ -49,7 +49,7 @@ uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
@ -101,7 +101,6 @@ PRIMARY KEY (id)
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
db.SetDebug(true)
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserDetail).
|
||||
With(User{}.UserScores).
|
||||
@ -112,5 +111,531 @@ PRIMARY KEY (id)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 3)
|
||||
t.Assert(user.UserDetail.Address, `address_3`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 3)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 3)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.With(user).
|
||||
With(user.UserDetail).
|
||||
With(user.UserScores).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.With(User{}).
|
||||
With(UserDetail{}).
|
||||
With(UserScores{}).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
// With part attribute: UserDetail.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.With(user).
|
||||
With(user.UserDetail).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 0)
|
||||
})
|
||||
// With part attribute: UserScores.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.With(user).
|
||||
With(user.UserScores).
|
||||
Where("id", 4).
|
||||
Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.Assert(user.UserDetail, nil)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_With_ScanList(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserDetail).
|
||||
With(User{}.UserScores).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserDetail).
|
||||
With(User{}.UserScores).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
// With part attribute: UserDetail.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserDetail).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 0)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 0)
|
||||
})
|
||||
// With part attribute: UserScores.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.With(User{}).
|
||||
With(User{}.UserScores).
|
||||
Where("id", []int{3, 4}).
|
||||
Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.Assert(users[0].UserDetail, nil)
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.Assert(users[1].UserDetail, nil)
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Scan(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user *User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 3).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 3)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 3)
|
||||
t.Assert(user.UserDetail.Address, `address_3`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 3)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 3)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var user User
|
||||
err := db.Model(tableUser).WithAll().Where("id", 4).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 4)
|
||||
t.AssertNE(user.UserDetail, nil)
|
||||
t.Assert(user.UserDetail.Uid, 4)
|
||||
t.Assert(user.UserDetail.Address, `address_4`)
|
||||
t.Assert(len(user.UserScores), 5)
|
||||
t.Assert(user.UserScores[0].Uid, 4)
|
||||
t.Assert(user.UserScores[0].Score, 1)
|
||||
t.Assert(user.UserScores[4].Uid, 4)
|
||||
t.Assert(user.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_ScanList(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
tableUserDetail = "user_detail"
|
||||
tableUserScores = "user_scores"
|
||||
)
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
name varchar(45) NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUser)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUser)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
address varchar(45) NOT NULL,
|
||||
PRIMARY KEY (uid)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserDetail)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserDetail)
|
||||
|
||||
if _, err := db.Exec(fmt.Sprintf(`
|
||||
CREATE TABLE IF NOT EXISTS %s (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
uid int(10) unsigned NOT NULL,
|
||||
score int(10) unsigned NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
`, tableUserScores)); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
defer dropTable(tableUserScores)
|
||||
|
||||
type UserDetail struct {
|
||||
gmeta.Meta `orm:"table:user_detail"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `orm:"table:user_scores"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `orm:"table:user"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
UserDetail *UserDetail `orm:"with:uid=id"`
|
||||
UserScores []*UserScores `orm:"with:uid=id"`
|
||||
}
|
||||
|
||||
// Initialize the data.
|
||||
var err error
|
||||
for i := 1; i <= 5; i++ {
|
||||
// User.
|
||||
_, err = db.Insert(tableUser, g.Map{
|
||||
"id": i,
|
||||
"name": fmt.Sprintf(`name_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Detail.
|
||||
_, err = db.Insert(tableUserDetail, g.Map{
|
||||
"uid": i,
|
||||
"address": fmt.Sprintf(`address_%d`, i),
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
// Scores.
|
||||
for j := 1; j <= 5; j++ {
|
||||
_, err = db.Insert(tableUserScores, g.Map{
|
||||
"uid": i,
|
||||
"score": j,
|
||||
})
|
||||
gtest.Assert(err, nil)
|
||||
}
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
err := db.Model(tableUser).WithAll().Where("id", []int{3, 4}).Scan(&users)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Id, 3)
|
||||
t.Assert(users[0].Name, "name_3")
|
||||
t.AssertNE(users[0].UserDetail, nil)
|
||||
t.Assert(users[0].UserDetail.Uid, 3)
|
||||
t.Assert(users[0].UserDetail.Address, "address_3")
|
||||
t.Assert(len(users[0].UserScores), 5)
|
||||
t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
t.Assert(users[0].UserScores[0].Score, 1)
|
||||
t.Assert(users[0].UserScores[4].Uid, 3)
|
||||
t.Assert(users[0].UserScores[4].Score, 5)
|
||||
|
||||
t.Assert(users[1].Id, 4)
|
||||
t.Assert(users[1].Name, "name_4")
|
||||
t.AssertNE(users[1].UserDetail, nil)
|
||||
t.Assert(users[1].UserDetail.Uid, 4)
|
||||
t.Assert(users[1].UserDetail.Address, "address_4")
|
||||
t.Assert(len(users[1].UserScores), 5)
|
||||
t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
t.Assert(users[1].UserScores[0].Score, 1)
|
||||
t.Assert(users[1].UserScores[4].Uid, 4)
|
||||
t.Assert(users[1].UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
@ -821,7 +821,7 @@ func Test_DB_ToJson(t *testing.T) {
|
||||
|
||||
result = nil
|
||||
err = result.Structs(&users)
|
||||
t.AssertNE(err, nil)
|
||||
t.AssertNil(err)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
|
||||
@ -296,7 +296,7 @@ func Test_Structs_Empty(t *testing.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
t.AssertNil(err)
|
||||
users := make([]User, 10)
|
||||
t.AssertNE(all.Structs(&users), nil)
|
||||
t.Assert(all.Structs(&users), nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
all, err := db.Table(table).Where("id>100").All()
|
||||
|
||||
@ -91,12 +91,11 @@ 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 `pointer` should be type of struct/*struct.
|
||||
// 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.
|
||||
func TagMapField(pointer interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(pointer, priority)
|
||||
func TagMapField(object interface{}, priority []string) (map[string]*Field, error) {
|
||||
fields, err := TagFields(object, priority)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -120,19 +119,31 @@ func getFieldValues(value interface{}) ([]*Field, error) {
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
|
||||
for reflectKind == reflect.Ptr {
|
||||
if !reflectValue.IsValid() || reflectValue.IsNil() {
|
||||
// If pointer is type of *struct and nil, then automatically create a temporary struct.
|
||||
for {
|
||||
switch reflectKind {
|
||||
case reflect.Ptr:
|
||||
if !reflectValue.IsValid() || reflectValue.IsNil() {
|
||||
// If pointer is type of *struct and nil, then automatically create a temporary struct.
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
} else {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
case reflect.Array, reflect.Slice:
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
} else {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
default:
|
||||
goto exitLoop
|
||||
}
|
||||
}
|
||||
exitLoop:
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Struct {
|
||||
return nil, errors.New("given value should be type of struct/*struct")
|
||||
return nil, errors.New("given value should be either type of struct/*struct/[]struct/[]*struct")
|
||||
}
|
||||
var (
|
||||
structType = reflectValue.Type()
|
||||
|
||||
@ -12,6 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// StructType retrieves and returns the struct Type of specified struct/*struct.
|
||||
// The parameter `object` should be either type of struct/*struct/[]struct/[]*struct.
|
||||
func StructType(object interface{}) (*Type, error) {
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
|
||||
@ -10,6 +10,21 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// DefaultTrimChars are the characters which are stripped by Trim* functions in default.
|
||||
DefaultTrimChars = string([]byte{
|
||||
'\t', // Tab.
|
||||
'\v', // Vertical tab.
|
||||
'\n', // New line (line feed).
|
||||
'\r', // Carriage return.
|
||||
'\f', // New page.
|
||||
' ', // Ordinary space.
|
||||
0x00, // NUL-byte.
|
||||
0x85, // Delete.
|
||||
0xA0, // Non-breaking space.
|
||||
})
|
||||
)
|
||||
|
||||
// IsLetterUpper checks whether the given byte b is in upper case.
|
||||
func IsLetterUpper(b byte) bool {
|
||||
if b >= byte('A') && b <= byte('Z') {
|
||||
@ -92,3 +107,27 @@ func RemoveSymbols(s string) string {
|
||||
func EqualFoldWithoutChars(s1, s2 string) bool {
|
||||
return strings.EqualFold(RemoveSymbols(s1), RemoveSymbols(s2))
|
||||
}
|
||||
|
||||
// SplitAndTrim splits string <str> by a string <delimiter> to an array,
|
||||
// and calls Trim to every element of this array. It ignores the elements
|
||||
// which are empty after Trim.
|
||||
func SplitAndTrim(str, delimiter string, characterMask ...string) []string {
|
||||
array := make([]string, 0)
|
||||
for _, v := range strings.Split(str, delimiter) {
|
||||
v = Trim(v, characterMask...)
|
||||
if v != "" {
|
||||
array = append(array, v)
|
||||
}
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Trim strips whitespace (or other characters) from the beginning and end of a string.
|
||||
// The optional parameter <characterMask> specifies the additional stripped characters.
|
||||
func Trim(str string, characterMask ...string) string {
|
||||
trimChars := DefaultTrimChars
|
||||
if len(characterMask) > 0 {
|
||||
trimChars += characterMask[0]
|
||||
}
|
||||
return strings.Trim(str, trimChars)
|
||||
}
|
||||
|
||||
@ -7,32 +7,14 @@
|
||||
package gstr
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// defaultTrimChars are the characters which are stripped by Trim* functions in default.
|
||||
defaultTrimChars = string([]byte{
|
||||
'\t', // Tab.
|
||||
'\v', // Vertical tab.
|
||||
'\n', // New line (line feed).
|
||||
'\r', // Carriage return.
|
||||
'\f', // New page.
|
||||
' ', // Ordinary space.
|
||||
0x00, // NUL-byte.
|
||||
0x85, // Delete.
|
||||
0xA0, // Non-breaking space.
|
||||
})
|
||||
)
|
||||
|
||||
// Trim strips whitespace (or other characters) from the beginning and end of a string.
|
||||
// The optional parameter <characterMask> specifies the additional stripped characters.
|
||||
func Trim(str string, characterMask ...string) string {
|
||||
trimChars := defaultTrimChars
|
||||
if len(characterMask) > 0 {
|
||||
trimChars += characterMask[0]
|
||||
}
|
||||
return strings.Trim(str, trimChars)
|
||||
return utils.Trim(str, characterMask...)
|
||||
}
|
||||
|
||||
// TrimStr strips all of the given <cut> string from the beginning and end of a string.
|
||||
@ -43,7 +25,7 @@ func TrimStr(str string, cut string, count ...int) string {
|
||||
|
||||
// TrimLeft strips whitespace (or other characters) from the beginning of a string.
|
||||
func TrimLeft(str string, characterMask ...string) string {
|
||||
trimChars := defaultTrimChars
|
||||
trimChars := utils.DefaultTrimChars
|
||||
if len(characterMask) > 0 {
|
||||
trimChars += characterMask[0]
|
||||
}
|
||||
@ -69,7 +51,7 @@ func TrimLeftStr(str string, cut string, count ...int) string {
|
||||
|
||||
// TrimRight strips whitespace (or other characters) from the end of a string.
|
||||
func TrimRight(str string, characterMask ...string) string {
|
||||
trimChars := defaultTrimChars
|
||||
trimChars := utils.DefaultTrimChars
|
||||
if len(characterMask) > 0 {
|
||||
trimChars += characterMask[0]
|
||||
}
|
||||
@ -97,7 +79,7 @@ func TrimRightStr(str string, cut string, count ...int) string {
|
||||
|
||||
// TrimAll trims all characters in string `str`.
|
||||
func TrimAll(str string, characterMask ...string) string {
|
||||
trimChars := defaultTrimChars
|
||||
trimChars := utils.DefaultTrimChars
|
||||
if len(characterMask) > 0 {
|
||||
trimChars += characterMask[0]
|
||||
}
|
||||
|
||||
@ -232,7 +232,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
rvField reflect.Value
|
||||
dataMap = make(map[string]interface{}) // result map.
|
||||
reflectType = reflectValue.Type() // attribute value type.
|
||||
name = "" // name may be the tag name or the struct attribute name.
|
||||
mapKey = "" // mapKey may be the tag name or the struct attribute name.
|
||||
)
|
||||
for i := 0; i < reflectValue.NumField(); i++ {
|
||||
rtField = reflectType.Field(i)
|
||||
@ -242,32 +242,32 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
if !utils.IsLetterUpper(fieldName[0]) {
|
||||
continue
|
||||
}
|
||||
name = ""
|
||||
mapKey = ""
|
||||
fieldTag := rtField.Tag
|
||||
for _, tag := range tags {
|
||||
if name = fieldTag.Get(tag); name != "" {
|
||||
if mapKey = fieldTag.Get(tag); mapKey != "" {
|
||||
break
|
||||
}
|
||||
}
|
||||
if name == "" {
|
||||
name = fieldName
|
||||
if mapKey == "" {
|
||||
mapKey = fieldName
|
||||
} else {
|
||||
// Support json tag feature: -, omitempty
|
||||
name = strings.TrimSpace(name)
|
||||
if name == "-" {
|
||||
mapKey = strings.TrimSpace(mapKey)
|
||||
if mapKey == "-" {
|
||||
continue
|
||||
}
|
||||
array := strings.Split(name, ",")
|
||||
array := strings.Split(mapKey, ",")
|
||||
if len(array) > 1 {
|
||||
switch strings.TrimSpace(array[1]) {
|
||||
case "omitempty":
|
||||
if empty.IsEmpty(rvField.Interface()) {
|
||||
continue
|
||||
} else {
|
||||
name = strings.TrimSpace(array[0])
|
||||
mapKey = strings.TrimSpace(array[0])
|
||||
}
|
||||
default:
|
||||
name = strings.TrimSpace(array[0])
|
||||
mapKey = strings.TrimSpace(array[0])
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -284,7 +284,7 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
switch rvAttrKind {
|
||||
case reflect.Struct:
|
||||
var (
|
||||
hasNoTag = name == fieldName
|
||||
hasNoTag = mapKey == fieldName
|
||||
rvAttrInterface = rvAttrField.Interface()
|
||||
)
|
||||
if hasNoTag && rtField.Anonymous {
|
||||
@ -296,41 +296,41 @@ func doMapConvertForMapOrStructValue(isRoot bool, value interface{}, recursive b
|
||||
dataMap[k] = v
|
||||
}
|
||||
} else {
|
||||
dataMap[name] = rvAttrInterface
|
||||
dataMap[mapKey] = rvAttrInterface
|
||||
}
|
||||
} else if !hasNoTag && rtField.Anonymous {
|
||||
// It means this attribute field has desired tag.
|
||||
dataMap[name] = doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...)
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, true, tags...)
|
||||
} else {
|
||||
dataMap[name] = doMapConvertForMapOrStructValue(false, rvAttrInterface, false, tags...)
|
||||
dataMap[mapKey] = doMapConvertForMapOrStructValue(false, rvAttrInterface, false, tags...)
|
||||
}
|
||||
|
||||
// The struct attribute is type of slice.
|
||||
case reflect.Array, reflect.Slice:
|
||||
length := rvField.Len()
|
||||
if length == 0 {
|
||||
dataMap[name] = rvField.Interface()
|
||||
dataMap[mapKey] = rvField.Interface()
|
||||
break
|
||||
}
|
||||
array := make([]interface{}, length)
|
||||
for i := 0; i < length; i++ {
|
||||
array[i] = doMapConvertForMapOrStructValue(false, rvField.Index(i), recursive, tags...)
|
||||
}
|
||||
dataMap[name] = array
|
||||
dataMap[mapKey] = array
|
||||
|
||||
default:
|
||||
if rvField.IsValid() {
|
||||
dataMap[name] = reflectValue.Field(i).Interface()
|
||||
dataMap[mapKey] = reflectValue.Field(i).Interface()
|
||||
} else {
|
||||
dataMap[name] = nil
|
||||
dataMap[mapKey] = nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// No recursive map value converting
|
||||
if rvField.IsValid() {
|
||||
dataMap[name] = reflectValue.Field(i).Interface()
|
||||
dataMap[mapKey] = reflectValue.Field(i).Interface()
|
||||
} else {
|
||||
dataMap[name] = nil
|
||||
dataMap[mapKey] = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -32,17 +32,32 @@ import (
|
||||
// in mapping procedure to do the matching.
|
||||
// It ignores the map key, if it does not match.
|
||||
func Struct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return doStruct(params, pointer, mapping...)
|
||||
var keyToAttributeNameMapping map[string]string
|
||||
if len(mapping) > 0 {
|
||||
keyToAttributeNameMapping = mapping[0]
|
||||
}
|
||||
return doStruct(params, pointer, keyToAttributeNameMapping, "")
|
||||
}
|
||||
|
||||
// StructTag acts as Struct but also with support for priority tag feature, which retrieves the
|
||||
// specified tags for `params` key-value items to struct attribute names mapping.
|
||||
// The parameter `priorityTag` supports multiple tags that can be joined with char ','.
|
||||
func StructTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
|
||||
return doStruct(params, pointer, nil, priorityTag)
|
||||
}
|
||||
|
||||
// StructDeep do Struct function recursively.
|
||||
// Deprecated, use Struct instead.
|
||||
func StructDeep(params interface{}, pointer interface{}, mapping ...map[string]string) error {
|
||||
return doStruct(params, pointer, mapping...)
|
||||
var keyToAttributeNameMapping map[string]string
|
||||
if len(mapping) > 0 {
|
||||
keyToAttributeNameMapping = mapping[0]
|
||||
}
|
||||
return doStruct(params, pointer, keyToAttributeNameMapping, "")
|
||||
}
|
||||
|
||||
// doStruct is the core internal converting function for any data to struct.
|
||||
func doStruct(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
func doStruct(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
|
||||
if params == nil {
|
||||
// If <params> is nil, no conversion.
|
||||
return nil
|
||||
@ -179,7 +194,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
continue
|
||||
}
|
||||
}
|
||||
if err = doStruct(paramsMap, elemFieldValue, mapping...); err != nil {
|
||||
if err = doStruct(paramsMap, elemFieldValue, mapping, priorityTag); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
@ -193,13 +208,26 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
|
||||
// The key of the tagMap is the attribute name of the struct,
|
||||
// and the value is its replaced tag name for later comparison to improve performance.
|
||||
tagMap := make(map[string]string)
|
||||
tagToNameMap, err := structs.TagMapName(pointerElemReflectValue, StructTagPriority)
|
||||
var (
|
||||
tagMap = make(map[string]string)
|
||||
priorityTagArray []string
|
||||
)
|
||||
if priorityTag != "" {
|
||||
priorityTagArray = append(utils.SplitAndTrim(priorityTag, ","), StructTagPriority...)
|
||||
} else {
|
||||
priorityTagArray = StructTagPriority
|
||||
}
|
||||
tagToNameMap, err := structs.TagMapName(pointerElemReflectValue, priorityTagArray)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for k, v := range tagToNameMap {
|
||||
tagMap[v] = utils.RemoveSymbols(k)
|
||||
for tagName, attributeName := range tagToNameMap {
|
||||
// If there's something else in the tag string,
|
||||
// it uses the first part which is split using char ','.
|
||||
// Eg:
|
||||
// orm:"id, priority"
|
||||
// orm:"name, with:uid=id"
|
||||
tagMap[attributeName] = utils.RemoveSymbols(strings.Split(tagName, ",")[0])
|
||||
}
|
||||
|
||||
var (
|
||||
@ -209,8 +237,8 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
for mapK, mapV := range paramsMap {
|
||||
attrName = ""
|
||||
// It firstly checks the passed mapping rules.
|
||||
if len(mapping) > 0 && len(mapping[0]) > 0 {
|
||||
if passedAttrKey, ok := mapping[0][mapK]; ok {
|
||||
if len(mapping) > 0 {
|
||||
if passedAttrKey, ok := mapping[mapK]; ok {
|
||||
attrName = passedAttrKey
|
||||
}
|
||||
}
|
||||
@ -254,7 +282,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
}
|
||||
// Mark it done.
|
||||
doneMap[attrName] = struct{}{}
|
||||
if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping...); err != nil {
|
||||
if err := bindVarToStructAttr(pointerElemReflectValue, attrName, mapV, mapping, priorityTag); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -262,7 +290,7 @@ func doStruct(params interface{}, pointer interface{}, mapping ...map[string]str
|
||||
}
|
||||
|
||||
// bindVarToStructAttr sets value to struct object attribute by name.
|
||||
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping ...map[string]string) (err error) {
|
||||
func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, mapping map[string]string, priorityTag string) (err error) {
|
||||
structFieldValue := elem.FieldByName(name)
|
||||
if !structFieldValue.IsValid() {
|
||||
return nil
|
||||
@ -273,7 +301,7 @@ func bindVarToStructAttr(elem reflect.Value, name string, value interface{}, map
|
||||
}
|
||||
defer func() {
|
||||
if e := recover(); e != nil {
|
||||
if err = bindVarToReflectValue(structFieldValue, value, mapping...); err != nil {
|
||||
if err = bindVarToReflectValue(structFieldValue, value, mapping, priorityTag); err != nil {
|
||||
err = gerror.Wrapf(err, `error binding value to attribute "%s"`, name)
|
||||
}
|
||||
}
|
||||
@ -322,7 +350,7 @@ func bindVarToReflectValueWithInterfaceCheck(reflectValue reflect.Value, value i
|
||||
}
|
||||
|
||||
// bindVarToReflectValue sets <value> to reflect value object <structFieldValue>.
|
||||
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping ...map[string]string) (err error) {
|
||||
func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, mapping map[string]string, priorityTag string) (err error) {
|
||||
if err, ok := bindVarToReflectValueWithInterfaceCheck(structFieldValue, value); ok {
|
||||
return err
|
||||
}
|
||||
@ -342,7 +370,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
|
||||
switch kind {
|
||||
case reflect.Struct:
|
||||
// Recursively converting for struct attribute.
|
||||
if err := doStruct(value, structFieldValue); err != nil {
|
||||
if err := doStruct(value, structFieldValue, nil, ""); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
structFieldValue.Set(reflect.ValueOf(value).Convert(structFieldValue.Type()))
|
||||
}
|
||||
@ -359,14 +387,14 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
|
||||
for i := 0; i < v.Len(); i++ {
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
if err := doStruct(v.Index(i).Interface(), e); err != nil {
|
||||
if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
|
||||
}
|
||||
a.Index(i).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
if err := doStruct(v.Index(i).Interface(), e); err != nil {
|
||||
if err := doStruct(v.Index(i).Interface(), e, nil, ""); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(v.Index(i).Interface()).Convert(t))
|
||||
}
|
||||
@ -379,14 +407,14 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
|
||||
t := a.Index(0).Type()
|
||||
if t.Kind() == reflect.Ptr {
|
||||
e := reflect.New(t.Elem()).Elem()
|
||||
if err := doStruct(value, e); err != nil {
|
||||
if err := doStruct(value, e, nil, ""); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(value).Convert(t))
|
||||
}
|
||||
a.Index(0).Set(e.Addr())
|
||||
} else {
|
||||
e := reflect.New(t).Elem()
|
||||
if err := doStruct(value, e); err != nil {
|
||||
if err := doStruct(value, e, nil, ""); err != nil {
|
||||
// Note there's reflect conversion mechanism here.
|
||||
e.Set(reflect.ValueOf(value).Convert(t))
|
||||
}
|
||||
@ -402,7 +430,7 @@ func bindVarToReflectValue(structFieldValue reflect.Value, value interface{}, ma
|
||||
return err
|
||||
}
|
||||
elem := item.Elem()
|
||||
if err = bindVarToReflectValue(elem, value, mapping...); err == nil {
|
||||
if err = bindVarToReflectValue(elem, value, mapping, priorityTag); err == nil {
|
||||
structFieldValue.Set(elem.Addr())
|
||||
}
|
||||
|
||||
|
||||
@ -14,13 +14,28 @@ import (
|
||||
|
||||
// Structs converts any slice to given struct slice.
|
||||
func Structs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return doStructs(params, pointer, mapping...)
|
||||
var keyToAttributeNameMapping map[string]string
|
||||
if len(mapping) > 0 {
|
||||
keyToAttributeNameMapping = mapping[0]
|
||||
}
|
||||
return doStructs(params, pointer, keyToAttributeNameMapping, "")
|
||||
}
|
||||
|
||||
// StructsTag acts as Structs but also with support for priority tag feature, which retrieves the
|
||||
// specified tags for `params` key-value items to struct attribute names mapping.
|
||||
// The parameter `priorityTag` supports multiple tags that can be joined with char ','.
|
||||
func StructsTag(params interface{}, pointer interface{}, priorityTag string) (err error) {
|
||||
return doStructs(params, pointer, nil, priorityTag)
|
||||
}
|
||||
|
||||
// StructsDeep converts any slice to given struct slice recursively.
|
||||
// Deprecated, use Structs instead.
|
||||
func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
return doStructs(params, pointer, mapping...)
|
||||
var keyToAttributeNameMapping map[string]string
|
||||
if len(mapping) > 0 {
|
||||
keyToAttributeNameMapping = mapping[0]
|
||||
}
|
||||
return doStructs(params, pointer, keyToAttributeNameMapping, "")
|
||||
}
|
||||
|
||||
// doStructs converts any slice to given struct slice.
|
||||
@ -30,7 +45,7 @@ func StructsDeep(params interface{}, pointer interface{}, mapping ...map[string]
|
||||
// The parameter <pointer> should be type of pointer to slice of struct.
|
||||
// Note that if <pointer> is a pointer to another pointer of type of slice of struct,
|
||||
// it will create the struct/pointer internally.
|
||||
func doStructs(params interface{}, pointer interface{}, mapping ...map[string]string) (err error) {
|
||||
func doStructs(params interface{}, pointer interface{}, mapping map[string]string, priorityTag string) (err error) {
|
||||
if params == nil {
|
||||
// If <params> is nil, no conversion.
|
||||
return nil
|
||||
@ -102,11 +117,13 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st
|
||||
for i := 0; i < len(paramsMaps); i++ {
|
||||
var tempReflectValue reflect.Value
|
||||
if i < pointerRvLength {
|
||||
// Might be nil.
|
||||
tempReflectValue = pointerRvElem.Index(i).Elem()
|
||||
} else {
|
||||
}
|
||||
if !tempReflectValue.IsValid() {
|
||||
tempReflectValue = reflect.New(itemType.Elem()).Elem()
|
||||
}
|
||||
if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil {
|
||||
if err = doStruct(paramsMaps[i], tempReflectValue, mapping, priorityTag); err != nil {
|
||||
return err
|
||||
}
|
||||
reflectElemArray.Index(i).Set(tempReflectValue.Addr())
|
||||
@ -120,7 +137,7 @@ func doStructs(params interface{}, pointer interface{}, mapping ...map[string]st
|
||||
} else {
|
||||
tempReflectValue = reflect.New(itemType).Elem()
|
||||
}
|
||||
if err = Struct(paramsMaps[i], tempReflectValue, mapping...); err != nil {
|
||||
if err = doStruct(paramsMaps[i], tempReflectValue, mapping, priorityTag); err != nil {
|
||||
return err
|
||||
}
|
||||
reflectElemArray.Index(i).Set(tempReflectValue)
|
||||
|
||||
77
util/gconv/gconv_z_unit_struct_tag_test.go
Normal file
77
util/gconv/gconv_z_unit_struct_tag_test.go
Normal file
@ -0,0 +1,77 @@
|
||||
// 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,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_StructTag(t *testing.T) {
|
||||
type User struct {
|
||||
Uid int
|
||||
Name string
|
||||
Pass1 string `orm:"password1"`
|
||||
Pass2 string `orm:"password2"`
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
params1 := g.Map{
|
||||
"uid": 1,
|
||||
"Name": "john",
|
||||
"password1": "123",
|
||||
"password2": "456",
|
||||
}
|
||||
if err := gconv.Struct(params1, user); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(user, &User{
|
||||
Uid: 1,
|
||||
Name: "john",
|
||||
Pass1: "",
|
||||
Pass2: "",
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
params1 := g.Map{
|
||||
"uid": 1,
|
||||
"Name": "john",
|
||||
"password1": "123",
|
||||
"password2": "456",
|
||||
}
|
||||
if err := gconv.StructTag(params1, user, "orm"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(user, &User{
|
||||
Uid: 1,
|
||||
Name: "john",
|
||||
Pass1: "123",
|
||||
Pass2: "456",
|
||||
})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
user := new(User)
|
||||
params2 := g.Map{
|
||||
"uid": 2,
|
||||
"name": "smith",
|
||||
"password1": "111",
|
||||
"password2": "222",
|
||||
}
|
||||
if err := gconv.StructTag(params2, user, "orm"); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
t.Assert(user, &User{
|
||||
Uid: 2,
|
||||
Name: "smith",
|
||||
Pass1: "111",
|
||||
Pass2: "222",
|
||||
})
|
||||
})
|
||||
}
|
||||
@ -7,14 +7,13 @@
|
||||
package gconv_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
func Test_Struct_Basic1(t *testing.T) {
|
||||
@ -904,6 +903,89 @@ func Test_Struct_Embedded(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_Slice(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []int
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []int32
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []int64
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []uint
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []uint32
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []uint64
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []float32
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []float64
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_To_Struct(t *testing.T) {
|
||||
var TestA struct {
|
||||
Id int `p:"id"`
|
||||
|
||||
@ -14,90 +14,7 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
func Test_Struct_Slice(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []int
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []int32
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []int64
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []uint
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []uint32
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []uint64
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []float32
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Scores []float64
|
||||
}
|
||||
user := new(User)
|
||||
array := g.Slice{1, 2, 3}
|
||||
err := gconv.Struct(g.Map{"scores": array}, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Scores, array)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Struct_SliceWithTag(t *testing.T) {
|
||||
func Test_Structs_WithTag(t *testing.T) {
|
||||
type User struct {
|
||||
Uid int `json:"id"`
|
||||
NickName string `json:"name"`
|
||||
@ -144,6 +61,97 @@ func Test_Struct_SliceWithTag(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_WithoutTag(t *testing.T) {
|
||||
type User struct {
|
||||
Uid int
|
||||
NickName string
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []User
|
||||
params := g.Slice{
|
||||
g.Map{
|
||||
"uid": 1,
|
||||
"nick-name": "name1",
|
||||
},
|
||||
g.Map{
|
||||
"uid": 2,
|
||||
"nick-name": "name2",
|
||||
},
|
||||
}
|
||||
err := gconv.Structs(params, &users)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Uid, 1)
|
||||
t.Assert(users[0].NickName, "name1")
|
||||
t.Assert(users[1].Uid, 2)
|
||||
t.Assert(users[1].NickName, "name2")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var users []*User
|
||||
params := g.Slice{
|
||||
g.Map{
|
||||
"uid": 1,
|
||||
"nick-name": "name1",
|
||||
},
|
||||
g.Map{
|
||||
"uid": 2,
|
||||
"nick-name": "name2",
|
||||
},
|
||||
}
|
||||
err := gconv.Structs(params, &users)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(len(users), 2)
|
||||
t.Assert(users[0].Uid, 1)
|
||||
t.Assert(users[0].NickName, "name1")
|
||||
t.Assert(users[1].Uid, 2)
|
||||
t.Assert(users[1].NickName, "name2")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_SliceParameter(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Uid int
|
||||
NickName string
|
||||
}
|
||||
var users []User
|
||||
params := g.Slice{
|
||||
g.Map{
|
||||
"uid": 1,
|
||||
"nick-name": "name1",
|
||||
},
|
||||
g.Map{
|
||||
"uid": 2,
|
||||
"nick-name": "name2",
|
||||
},
|
||||
}
|
||||
err := gconv.Structs(params, users)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Uid int
|
||||
NickName string
|
||||
}
|
||||
type A struct {
|
||||
Users []User
|
||||
}
|
||||
var a A
|
||||
params := g.Slice{
|
||||
g.Map{
|
||||
"uid": 1,
|
||||
"nick-name": "name1",
|
||||
},
|
||||
g.Map{
|
||||
"uid": 2,
|
||||
"nick-name": "name2",
|
||||
},
|
||||
}
|
||||
err := gconv.Structs(params, a.Users)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_DirectReflectSet(t *testing.T) {
|
||||
type A struct {
|
||||
Id int
|
||||
@ -175,7 +183,7 @@ func Test_Structs_DirectReflectSet(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Structs_SliceIntAttribute(t *testing.T) {
|
||||
func Test_Structs_IntSliceAttribute(t *testing.T) {
|
||||
type A struct {
|
||||
Id []int
|
||||
}
|
||||
@ -55,7 +55,7 @@ func Test_Time_Slice_Attribute(t *testing.T) {
|
||||
"arr": g.Slice{"2021-01-12 12:34:56", "2021-01-12 12:34:57"},
|
||||
"one": "2021-01-12 12:34:58",
|
||||
}, &s)
|
||||
t.Assert(err, nil)
|
||||
t.AssertNil(err)
|
||||
t.Assert(s.One, "2021-01-12 12:34:58")
|
||||
t.Assert(s.Arr[0], "2021-01-12 12:34:56")
|
||||
t.Assert(s.Arr[1], "2021-01-12 12:34:57")
|
||||
|
||||
Reference in New Issue
Block a user