mirror of
https://gitee.com/johng/gf
synced 2026-07-06 21:45:34 +08:00
add package gmeta for embedded struct of meta data feature
This commit is contained in:
@ -35,6 +35,7 @@ type DB interface {
|
||||
// The DB interface is designed not only for
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "Table" is not proper for that purpose any more.
|
||||
// Deprecated, use Model instead.
|
||||
Table(table ...string) *Model
|
||||
Model(table ...string) *Model
|
||||
Schema(schema string) *Schema
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"reflect"
|
||||
"regexp"
|
||||
@ -47,10 +48,16 @@ type apiMapStrAny interface {
|
||||
MapStrAny() map[string]interface{}
|
||||
}
|
||||
|
||||
// apiTableName is the interface for retrieving table name fro struct.
|
||||
type apiTableName interface {
|
||||
TableName() string
|
||||
}
|
||||
|
||||
const (
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
OrmTagForStruct = "orm"
|
||||
OrmTagForUnique = "unique"
|
||||
OrmTagForPrimary = "primary"
|
||||
metaDataNameForTable = "table"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -61,6 +68,26 @@ var (
|
||||
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
|
||||
)
|
||||
|
||||
// getTableNameFromObject retrieves and returns the table name from struct object.
|
||||
func getTableNameFromObject(object interface{}) string {
|
||||
if r, ok := object.(apiTableName); ok {
|
||||
// Use the interface value.
|
||||
return r.TableName()
|
||||
} else if table := gmeta.Get(object, metaDataNameForTable); !table.IsEmpty() {
|
||||
// User meta data tag "table".
|
||||
return table.String()
|
||||
} else {
|
||||
// Use the struct name of snake case.
|
||||
if t, err := structs.StructType(object); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
return gstr.CaseSnakeFirstUpper(
|
||||
gstr.StrEx(t.String(), "."),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ListItemValues retrieves and returns the elements of all item struct/map with key <key>.
|
||||
// Note that the parameter <list> should be type of slice which contains elements of map or struct,
|
||||
// or else it returns an empty slice.
|
||||
@ -752,9 +779,16 @@ func convertMapToStruct(data map[string]interface{}, pointer interface{}) error
|
||||
return err
|
||||
}
|
||||
// It retrieves and returns the mapping between orm tag and the struct attribute name.
|
||||
mapping := make(map[string]string)
|
||||
var (
|
||||
mapping = make(map[string]string)
|
||||
tagFieldName string
|
||||
)
|
||||
for tag, attr := range tagNameMap {
|
||||
mapping[strings.Split(tag, ",")[0]] = attr
|
||||
tagFieldName = strings.Split(tag, ",")[0]
|
||||
if !gregex.IsMatchString(regularFieldNameRegPattern, tagFieldName) {
|
||||
continue
|
||||
}
|
||||
mapping[tagFieldName] = attr
|
||||
}
|
||||
return gconv.Struct(data, pointer, mapping)
|
||||
}
|
||||
|
||||
@ -25,6 +25,7 @@ type Model struct {
|
||||
tables string // Operation table names, which can be more than one table names and aliases, like: "user", "user u", "user u, user_detail ud".
|
||||
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.
|
||||
extraArgs []interface{} // Extra custom arguments for sql.
|
||||
whereHolder []*whereHolder // Condition strings for where operation.
|
||||
groupBy string // Used for "group by" statement.
|
||||
@ -53,9 +54,13 @@ type whereHolder struct {
|
||||
}
|
||||
|
||||
const (
|
||||
OPTION_OMITEMPTY = 1
|
||||
// Deprecated, use OptionOmitEmpty instead.
|
||||
OPTION_OMITEMPTY = 1
|
||||
// Deprecated, use OptionAllowEmpty instead..
|
||||
OPTION_ALLOWEMPTY = 2
|
||||
|
||||
OptionOmitEmpty = 1
|
||||
OptionAllowEmpty = 2
|
||||
linkTypeMaster = 1
|
||||
linkTypeSlave = 2
|
||||
whereHolderWhere = 1
|
||||
@ -63,15 +68,22 @@ const (
|
||||
whereHolderOr = 3
|
||||
)
|
||||
|
||||
// Table creates and returns a new ORM model from given schema.
|
||||
// The parameter <table> can be more than one table names, and also alias name, like:
|
||||
// 1. Table names:
|
||||
// Table("user")
|
||||
// Table("user u")
|
||||
// Table("user, user_detail")
|
||||
// Table("user u, user_detail ud")
|
||||
// 2. Table name with alias: Table("user", "u")
|
||||
// Table is alias of Core.Model.
|
||||
// See Core.Model.
|
||||
// Deprecated, use Model instead.
|
||||
func (c *Core) Table(table ...string) *Model {
|
||||
return c.DB.Model(table...)
|
||||
}
|
||||
|
||||
// Model creates and returns a new ORM model from given schema.
|
||||
// The parameter <table> can be more than one table names, and also alias name, like:
|
||||
// 1. Model names:
|
||||
// Model("user")
|
||||
// Model("user u")
|
||||
// Model("user, user_detail")
|
||||
// Model("user u, user_detail ud")
|
||||
// 2. Model name with alias: Model("user", "u")
|
||||
func (c *Core) Model(table ...string) *Model {
|
||||
tables := ""
|
||||
if len(table) > 1 {
|
||||
tables = fmt.Sprintf(
|
||||
@ -79,8 +91,6 @@ func (c *Core) Table(table ...string) *Model {
|
||||
)
|
||||
} else if len(table) == 1 {
|
||||
tables = c.DB.QuotePrefixTableName(table[0])
|
||||
} else {
|
||||
panic("table cannot be empty")
|
||||
}
|
||||
return &Model{
|
||||
db: c.DB,
|
||||
@ -89,31 +99,25 @@ func (c *Core) Table(table ...string) *Model {
|
||||
fields: "*",
|
||||
start: -1,
|
||||
offset: -1,
|
||||
option: OPTION_ALLOWEMPTY,
|
||||
option: OptionAllowEmpty,
|
||||
}
|
||||
}
|
||||
|
||||
// Model is alias of Core.Table.
|
||||
// See Core.Table.
|
||||
func (c *Core) Model(table ...string) *Model {
|
||||
return c.DB.Table(table...)
|
||||
// Table is alias of tx.Model.
|
||||
// Deprecated, use Model instead.
|
||||
func (tx *TX) Table(table ...string) *Model {
|
||||
return tx.Model(table...)
|
||||
}
|
||||
|
||||
// Table acts like Core.Table except it operates on transaction.
|
||||
// See Core.Table.
|
||||
func (tx *TX) Table(table ...string) *Model {
|
||||
model := tx.db.Table(table...)
|
||||
// Model acts like Core.Model except it operates on transaction.
|
||||
// See Core.Model.
|
||||
func (tx *TX) Model(table ...string) *Model {
|
||||
model := tx.db.Model(table...)
|
||||
model.db = tx.db
|
||||
model.tx = tx
|
||||
return model
|
||||
}
|
||||
|
||||
// Model is alias of tx.Table.
|
||||
// See tx.Table.
|
||||
func (tx *TX) Model(table ...string) *Model {
|
||||
return tx.Table(table...)
|
||||
}
|
||||
|
||||
// Ctx sets the context for current operation.
|
||||
func (m *Model) Ctx(ctx context.Context) *Model {
|
||||
if ctx == nil {
|
||||
@ -175,7 +179,7 @@ func (m *Model) Clone() *Model {
|
||||
newModel = m.db.Table(m.tablesInit)
|
||||
}
|
||||
*newModel = *m
|
||||
// Deep copy slice attributes.
|
||||
// Shallow copy slice attributes.
|
||||
if n := len(m.extraArgs); n > 0 {
|
||||
newModel.extraArgs = make([]interface{}, n)
|
||||
copy(newModel.extraArgs, m.extraArgs)
|
||||
@ -184,6 +188,10 @@ func (m *Model) Clone() *Model {
|
||||
newModel.whereHolder = make([]*whereHolder, n)
|
||||
copy(newModel.whereHolder, m.whereHolder)
|
||||
}
|
||||
if n := len(m.withArray); n > 0 {
|
||||
newModel.withArray = make([]interface{}, n)
|
||||
copy(newModel.withArray, m.withArray)
|
||||
}
|
||||
return newModel
|
||||
}
|
||||
|
||||
|
||||
@ -13,15 +13,15 @@ func (m *Model) Option(option int) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// OptionOmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// OptionOmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
// Deprecated, use OmitEmpty instead.
|
||||
func (m *Model) OptionOmitEmpty() *Model {
|
||||
return m.Option(OPTION_OMITEMPTY)
|
||||
return m.Option(OptionOmitEmpty)
|
||||
}
|
||||
|
||||
// OmitEmpty sets OPTION_OMITEMPTY option for the model, which automatically filers
|
||||
// OmitEmpty sets OptionOmitEmpty option for the model, which automatically filers
|
||||
// the data and where attributes for empty values.
|
||||
func (m *Model) OmitEmpty() *Model {
|
||||
return m.Option(OPTION_OMITEMPTY)
|
||||
return m.Option(OptionOmitEmpty)
|
||||
}
|
||||
|
||||
@ -92,7 +92,7 @@ func (m *Model) doMappingAndFilterForInsertOrUpdateDataMap(data Map, allowOmitEm
|
||||
return nil, err
|
||||
}
|
||||
// Remove key-value pairs of which the value is empty.
|
||||
if allowOmitEmpty && m.option&OPTION_OMITEMPTY > 0 {
|
||||
if allowOmitEmpty && m.option&OptionOmitEmpty > 0 {
|
||||
tempMap := make(Map, len(data))
|
||||
for k, v := range data {
|
||||
if empty.IsEmpty(v) {
|
||||
@ -206,7 +206,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
case whereHolderWhere:
|
||||
if conditionWhere == "" {
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
conditionWhere = newWhere
|
||||
@ -218,7 +218,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
|
||||
case whereHolderAnd:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -233,7 +233,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
|
||||
case whereHolderOr:
|
||||
newWhere, newArgs := formatWhere(
|
||||
m.db, v.where, v.args, m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, v.where, v.args, m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(newWhere) > 0 {
|
||||
if len(conditionWhere) == 0 {
|
||||
@ -268,7 +268,7 @@ func (m *Model) formatCondition(limit1 bool, isCountStatement bool) (conditionWh
|
||||
// HAVING.
|
||||
if len(m.having) > 0 {
|
||||
havingStr, havingArgs := formatWhere(
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OPTION_OMITEMPTY > 0,
|
||||
m.db, m.having[0], gconv.Interfaces(m.having[1]), m.option&OptionOmitEmpty > 0,
|
||||
)
|
||||
if len(havingStr) > 0 {
|
||||
conditionExtra += " HAVING " + havingStr
|
||||
|
||||
17
database/gdb/gdb_model_with.go
Normal file
17
database/gdb/gdb_model_with.go
Normal file
@ -0,0 +1,17 @@
|
||||
// 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 gdb
|
||||
|
||||
func (m *Model) With(structAttrPointer interface{}) *Model {
|
||||
model := m.getModel()
|
||||
if m.tables == "" {
|
||||
m.tables = getTableNameFromObject(structAttrPointer)
|
||||
return model
|
||||
}
|
||||
model.withArray = append(model.withArray, structAttrPointer)
|
||||
return model
|
||||
}
|
||||
@ -226,7 +226,6 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
)
|
||||
} else {
|
||||
relationBindToSubAttrName = key
|
||||
|
||||
}
|
||||
}
|
||||
relationBindToSubAttrNameChecked = true
|
||||
|
||||
@ -6,311 +6,103 @@
|
||||
|
||||
package gdb_test
|
||||
|
||||
//func Test_Table_Relation_With(t *testing.T) {
|
||||
// var (
|
||||
// tableUser = "user_" + gtime.TimestampMicroStr()
|
||||
// tableUserDetail = "user_detail_" + gtime.TimestampMicroStr()
|
||||
// tableUserScores = "user_scores_" + gtime.TimestampMicroStr()
|
||||
// )
|
||||
// if _, err := db.Exec(fmt.Sprintf(`
|
||||
//CREATE TABLE %s (
|
||||
// uid int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
// name varchar(45) NOT NULL,
|
||||
// PRIMARY KEY (uid)
|
||||
//) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
// `, tableUser)); err != nil {
|
||||
// gtest.Error(err)
|
||||
// }
|
||||
// defer dropTable(tableUser)
|
||||
//
|
||||
// if _, err := db.Exec(fmt.Sprintf(`
|
||||
//CREATE TABLE %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 %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 {
|
||||
// Uid int `json:"uid"`
|
||||
// Address string `json:"address"`
|
||||
// }
|
||||
// type UserScores struct {
|
||||
// Id int `json:"id"`
|
||||
// Uid int `json:"uid"`
|
||||
// Score int `json:"score"`
|
||||
// }
|
||||
//
|
||||
// type User struct {
|
||||
// Id int `json:"id"`
|
||||
// Name string `json:"name"`
|
||||
// UserDetail *UserDetail `orm:"with:uid=uid"`
|
||||
// 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)
|
||||
// }
|
||||
// }
|
||||
// // MapKeyValue.
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All()
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(all.Len(), 2)
|
||||
// t.Assert(len(all.MapKeyValue("uid")), 2)
|
||||
// t.Assert(all.MapKeyValue("uid")["3"].Map()["uid"], 3)
|
||||
// t.Assert(all.MapKeyValue("uid")["4"].Map()["uid"], 4)
|
||||
// all, err = db.Table(tableUserScores).Where("uid", g.Slice{3, 4}).Order("id asc").All()
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(all.Len(), 10)
|
||||
// t.Assert(len(all.MapKeyValue("uid")), 2)
|
||||
// t.Assert(len(all.MapKeyValue("uid")["3"].Slice()), 5)
|
||||
// t.Assert(len(all.MapKeyValue("uid")["4"].Slice()), 5)
|
||||
// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["uid"], 3)
|
||||
// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[0])["score"], 1)
|
||||
// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["uid"], 3)
|
||||
// t.Assert(gconv.Map(all.MapKeyValue("uid")["3"].Slice()[4])["score"], 5)
|
||||
// })
|
||||
// // Result ScanList with struct elements and pointer attributes.
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// var users []Entity
|
||||
// // User
|
||||
// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All()
|
||||
// t.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "User")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users), 2)
|
||||
// t.Assert(users[0].User, &EntityUser{3, "name_3"})
|
||||
// t.Assert(users[1].User, &EntityUser{4, "name_4"})
|
||||
// // Detail
|
||||
// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"})
|
||||
// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"})
|
||||
// // Scores
|
||||
// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserScores", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users[0].UserScores), 5)
|
||||
// t.Assert(len(users[1].UserScores), 5)
|
||||
// t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[0].Score, 1)
|
||||
// t.Assert(users[0].UserScores[4].Score, 5)
|
||||
// t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[0].Score, 1)
|
||||
// t.Assert(users[1].UserScores[4].Score, 5)
|
||||
// })
|
||||
//
|
||||
// // Result ScanList with pointer elements and pointer attributes.
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// var users []*Entity
|
||||
// // User
|
||||
// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All()
|
||||
// t.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "User")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users), 2)
|
||||
// t.Assert(users[0].User, &EntityUser{3, "name_3"})
|
||||
// t.Assert(users[1].User, &EntityUser{4, "name_4"})
|
||||
// // Detail
|
||||
// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"})
|
||||
// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"})
|
||||
// // Scores
|
||||
// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserScores", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users[0].UserScores), 5)
|
||||
// t.Assert(len(users[1].UserScores), 5)
|
||||
// t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[0].Score, 1)
|
||||
// t.Assert(users[0].UserScores[4].Score, 5)
|
||||
// t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[0].Score, 1)
|
||||
// t.Assert(users[1].UserScores[4].Score, 5)
|
||||
// })
|
||||
//
|
||||
// // Result ScanList with struct elements and struct attributes.
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// type EntityUser struct {
|
||||
// Uid int `json:"uid"`
|
||||
// Name string `json:"name"`
|
||||
// }
|
||||
// type EntityUserDetail struct {
|
||||
// Uid int `json:"uid"`
|
||||
// Address string `json:"address"`
|
||||
// }
|
||||
// type EntityUserScores struct {
|
||||
// Id int `json:"id"`
|
||||
// Uid int `json:"uid"`
|
||||
// Score int `json:"score"`
|
||||
// }
|
||||
// type Entity struct {
|
||||
// User EntityUser
|
||||
// UserDetail EntityUserDetail
|
||||
// UserScores []EntityUserScores
|
||||
// }
|
||||
// var users []Entity
|
||||
// // User
|
||||
// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All()
|
||||
// t.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "User")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users), 2)
|
||||
// t.Assert(users[0].User, &EntityUser{3, "name_3"})
|
||||
// t.Assert(users[1].User, &EntityUser{4, "name_4"})
|
||||
// // Detail
|
||||
// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"})
|
||||
// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"})
|
||||
// // Scores
|
||||
// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserScores", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users[0].UserScores), 5)
|
||||
// t.Assert(len(users[1].UserScores), 5)
|
||||
// t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[0].Score, 1)
|
||||
// t.Assert(users[0].UserScores[4].Score, 5)
|
||||
// t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[0].Score, 1)
|
||||
// t.Assert(users[1].UserScores[4].Score, 5)
|
||||
// })
|
||||
//
|
||||
// // Result ScanList with pointer elements and struct attributes.
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// type EntityUser struct {
|
||||
// Uid int `json:"uid"`
|
||||
// Name string `json:"name"`
|
||||
// }
|
||||
// type EntityUserDetail struct {
|
||||
// Uid int `json:"uid"`
|
||||
// Address string `json:"address"`
|
||||
// }
|
||||
// type EntityUserScores struct {
|
||||
// Id int `json:"id"`
|
||||
// Uid int `json:"uid"`
|
||||
// Score int `json:"score"`
|
||||
// }
|
||||
// type Entity struct {
|
||||
// User EntityUser
|
||||
// UserDetail EntityUserDetail
|
||||
// UserScores []EntityUserScores
|
||||
// }
|
||||
// var users []*Entity
|
||||
//
|
||||
// // User
|
||||
// all, err := db.Table(tableUser).Where("uid", g.Slice{3, 4}).Order("uid asc").All()
|
||||
// t.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "User")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users), 2)
|
||||
// t.Assert(users[0].User, &EntityUser{3, "name_3"})
|
||||
// t.Assert(users[1].User, &EntityUser{4, "name_4"})
|
||||
// // Detail
|
||||
// all, err = db.Table(tableUserDetail).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("uid asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserDetail", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"})
|
||||
// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"})
|
||||
// // Scores
|
||||
// all, err = db.Table(tableUserScores).Where("uid", gdb.ListItemValues(users, "User", "Uid")).Order("id asc").All()
|
||||
// gtest.Assert(err, nil)
|
||||
// err = all.ScanList(&users, "UserScores", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
// t.Assert(len(users[0].UserScores), 5)
|
||||
// t.Assert(len(users[1].UserScores), 5)
|
||||
// t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[0].Score, 1)
|
||||
// t.Assert(users[0].UserScores[4].Score, 5)
|
||||
// t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[0].Score, 1)
|
||||
// t.Assert(users[1].UserScores[4].Score, 5)
|
||||
// })
|
||||
//
|
||||
// // Model ScanList with pointer elements and pointer attributes.
|
||||
// gtest.C(t, func(t *gtest.T) {
|
||||
// var users []*Entity
|
||||
// // User
|
||||
// err := db.Table(tableUser).
|
||||
// Where("uid", g.Slice{3, 4}).
|
||||
// Order("uid asc").
|
||||
// ScanList(&users, "User")
|
||||
// t.Assert(err, nil)
|
||||
// // Detail
|
||||
// err = db.Table(tableUserDetail).
|
||||
// Where("uid", gdb.ListItemValues(users, "User", "Uid")).
|
||||
// Order("uid asc").
|
||||
// ScanList(&users, "UserDetail", "User", "uid:Uid")
|
||||
// gtest.Assert(err, nil)
|
||||
// // Scores
|
||||
// err = db.Table(tableUserScores).
|
||||
// Where("uid", gdb.ListItemValues(users, "User", "Uid")).
|
||||
// Order("id asc").
|
||||
// ScanList(&users, "UserScores", "User", "uid:Uid")
|
||||
// t.Assert(err, nil)
|
||||
//
|
||||
// t.Assert(len(users), 2)
|
||||
// t.Assert(users[0].User, &EntityUser{3, "name_3"})
|
||||
// t.Assert(users[1].User, &EntityUser{4, "name_4"})
|
||||
//
|
||||
// t.Assert(users[0].UserDetail, &EntityUserDetail{3, "address_3"})
|
||||
// t.Assert(users[1].UserDetail, &EntityUserDetail{4, "address_4"})
|
||||
//
|
||||
// t.Assert(len(users[0].UserScores), 5)
|
||||
// t.Assert(len(users[1].UserScores), 5)
|
||||
// t.Assert(users[0].UserScores[0].Uid, 3)
|
||||
// t.Assert(users[0].UserScores[0].Score, 1)
|
||||
// t.Assert(users[0].UserScores[4].Score, 5)
|
||||
// t.Assert(users[1].UserScores[0].Uid, 4)
|
||||
// t.Assert(users[1].UserScores[0].Score, 1)
|
||||
// t.Assert(users[1].UserScores[4].Score, 5)
|
||||
// })
|
||||
//}
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Table_Relation_With(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user_with"
|
||||
tableUserDetail = "user_detail_with"
|
||||
tableUserScores = "user_scores_with"
|
||||
)
|
||||
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 `table:"user_detail_with"`
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
gmeta.Meta `table:"user_scores_with"`
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
gmeta.Meta `table:"user_with"`
|
||||
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().With(&user).Where("id", 3).Scan(&user)
|
||||
t.AssertNil(err)
|
||||
t.Assert(user.Id, 3)
|
||||
})
|
||||
}
|
||||
|
||||
@ -2013,7 +2013,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
r, err := db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{
|
||||
r, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": 0,
|
||||
"password": 0,
|
||||
@ -2033,7 +2033,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{
|
||||
_, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": 0,
|
||||
"password": 0,
|
||||
@ -2069,7 +2069,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
_, err := db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{
|
||||
_, err := db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{
|
||||
"id": 1,
|
||||
"passport": 0,
|
||||
"password": 0,
|
||||
@ -2106,7 +2106,7 @@ func Test_Model_Option_Map(t *testing.T) {
|
||||
n, _ := r.RowsAffected()
|
||||
t.Assert(n, 1)
|
||||
|
||||
_, err = db.Table(table).Option(gdb.OPTION_OMITEMPTY).Data(g.Map{"nickname": ""}).Where("id", 2).Update()
|
||||
_, err = db.Table(table).Option(gdb.OptionOmitEmpty).Data(g.Map{"nickname": ""}).Where("id", 2).Update()
|
||||
t.AssertNE(err, nil)
|
||||
|
||||
r, err = db.Table(table).OmitEmpty().Data(g.Map{"nickname": "", "password": "123"}).Where("id", 3).Update()
|
||||
|
||||
@ -92,15 +92,17 @@ func DB(name ...string) gdb.DB {
|
||||
}
|
||||
|
||||
// Table is alias of Model.
|
||||
func Table(tables string, db ...string) *gdb.Model {
|
||||
return DB(db...).Table(tables)
|
||||
// The database component is designed not only for
|
||||
// relational databases but also for NoSQL databases in the future. The name
|
||||
// "Table" is not proper for that purpose any more.
|
||||
// Deprecated, use Model instead.
|
||||
func Table(tables ...string) *gdb.Model {
|
||||
return DB().Table(tables...)
|
||||
}
|
||||
|
||||
// Model creates and returns a model from specified database or default database configuration.
|
||||
// The optional parameter <db> specifies the configuration group name of the database,
|
||||
// which is "default" in default.
|
||||
func Model(tables string, db ...string) *gdb.Model {
|
||||
return DB(db...).Model(tables)
|
||||
// Model creates and returns a model based on configuration of default database group.
|
||||
func Model(tables ...string) *gdb.Model {
|
||||
return DB().Model(tables...)
|
||||
}
|
||||
|
||||
// Redis returns an instance of redis client with specified configuration group name.
|
||||
|
||||
@ -13,6 +13,11 @@ import (
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Type wraps reflect.Type for additional features.
|
||||
type Type struct {
|
||||
reflect.Type
|
||||
}
|
||||
|
||||
// Field contains information of a struct field .
|
||||
type Field struct {
|
||||
value reflect.Value
|
||||
|
||||
55
internal/structs/structs_type.go
Normal file
55
internal/structs/structs_type.go
Normal file
@ -0,0 +1,55 @@
|
||||
// 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 structs
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/errors/gerror"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// StructType retrieves and returns the Type of specified struct/*struct.
|
||||
func StructType(structOrPointer interface{}) (*Type, error) {
|
||||
var (
|
||||
reflectValue reflect.Value
|
||||
reflectKind reflect.Kind
|
||||
reflectType reflect.Type
|
||||
)
|
||||
if rv, ok := structOrPointer.(reflect.Value); ok {
|
||||
reflectValue = rv
|
||||
} else {
|
||||
reflectValue = reflect.ValueOf(structOrPointer)
|
||||
}
|
||||
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.
|
||||
reflectValue = reflect.New(reflectValue.Type().Elem()).Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
} else {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
}
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Struct {
|
||||
return nil, gerror.Newf(
|
||||
`invalid parameter kind "%s", kind of "struct" is required`,
|
||||
reflectKind,
|
||||
)
|
||||
}
|
||||
reflectType = reflectValue.Type()
|
||||
return &Type{
|
||||
Type: reflectType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (t *Type) Signature() string {
|
||||
return t.PkgPath() + "/" + t.String()
|
||||
}
|
||||
@ -124,3 +124,39 @@ func Test_MapField(t *testing.T) {
|
||||
t.Assert(ok, true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StructType(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B
|
||||
}
|
||||
r, err := structs.StructType(new(A))
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.A`)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
B
|
||||
}
|
||||
r, err := structs.StructType(new(A).B)
|
||||
t.AssertNil(err)
|
||||
t.Assert(r.Signature(), `github.com/gogf/gf/internal/structs_test/structs_test.B`)
|
||||
})
|
||||
// Error.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type B struct {
|
||||
Name string
|
||||
}
|
||||
type A struct {
|
||||
*B
|
||||
}
|
||||
_, err := structs.StructType(new(A).B)
|
||||
t.AssertNE(err, nil)
|
||||
})
|
||||
}
|
||||
|
||||
@ -472,6 +472,15 @@ func Str(haystack string, needle string) string {
|
||||
return haystack[idx+len([]byte(needle))-1:]
|
||||
}
|
||||
|
||||
// StrEx returns part of <haystack> string starting from and excluding
|
||||
// the first occurrence of <needle> to the end of <haystack>.
|
||||
func StrEx(haystack string, needle string) string {
|
||||
if s := Str(haystack, needle); s != "" {
|
||||
return s[1:]
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Shuffle randomly shuffles a string.
|
||||
// It considers parameter <str> as unicode string.
|
||||
func Shuffle(str string) string {
|
||||
|
||||
@ -295,6 +295,14 @@ func Test_Str(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrEx(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(gstr.StrEx("name@example.com", "@"), "example.com")
|
||||
t.Assert(gstr.StrEx("name@example.com", ""), "")
|
||||
t.Assert(gstr.StrEx("name@example.com", "z"), "")
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Shuffle(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(len(gstr.Shuffle("123456")), 6)
|
||||
|
||||
94
util/gmeta/gmeta.go
Normal file
94
util/gmeta/gmeta.go
Normal file
@ -0,0 +1,94 @@
|
||||
// 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 gmeta provides embedded meta data feature for struct.
|
||||
package gmeta
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"github.com/gogf/gf/internal/structs"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Meta is used as an embedded attribute for struct to enabled meta data feature.
|
||||
type Meta struct{}
|
||||
|
||||
const (
|
||||
metaAttributeName = "Meta"
|
||||
)
|
||||
|
||||
var (
|
||||
metaDataCacheMap = gmap.NewStrAnyMap(true)
|
||||
)
|
||||
|
||||
// Data retrieves and returns all meta data from `object`.
|
||||
// It automatically parses the tag string from "Mata" attribute as its meta data.
|
||||
func Data(object interface{}) map[string]interface{} {
|
||||
reflectType, err := structs.StructType(object)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return metaDataCacheMap.GetOrSetFuncLock(reflectType.Signature(), func() interface{} {
|
||||
if field, ok := reflectType.FieldByName(metaAttributeName); ok {
|
||||
var (
|
||||
key string
|
||||
tag = field.Tag
|
||||
data = make(map[string]interface{})
|
||||
)
|
||||
for tag != "" {
|
||||
// Skip leading space.
|
||||
i := 0
|
||||
for i < len(tag) && tag[i] == ' ' {
|
||||
i++
|
||||
}
|
||||
tag = tag[i:]
|
||||
if tag == "" {
|
||||
break
|
||||
}
|
||||
// Scan to colon. A space, a quote or a control character is a syntax error.
|
||||
// Strictly speaking, control chars include the range [0x7f, 0x9f], not just
|
||||
// [0x00, 0x1f], but in practice, we ignore the multi-byte control characters
|
||||
// as it is simpler to inspect the tag's bytes than the tag's runes.
|
||||
i = 0
|
||||
for i < len(tag) && tag[i] > ' ' && tag[i] != ':' && tag[i] != '"' && tag[i] != 0x7f {
|
||||
i++
|
||||
}
|
||||
if i == 0 || i+1 >= len(tag) || tag[i] != ':' || tag[i+1] != '"' {
|
||||
break
|
||||
}
|
||||
key = string(tag[:i])
|
||||
tag = tag[i+1:]
|
||||
|
||||
// Scan quoted string to find value.
|
||||
i = 1
|
||||
for i < len(tag) && tag[i] != '"' {
|
||||
if tag[i] == '\\' {
|
||||
i++
|
||||
}
|
||||
i++
|
||||
}
|
||||
if i >= len(tag) {
|
||||
break
|
||||
}
|
||||
quotedValue := string(tag[:i+1])
|
||||
tag = tag[i+1:]
|
||||
value, err := strconv.Unquote(quotedValue)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
data[key] = value
|
||||
}
|
||||
return data
|
||||
}
|
||||
return map[string]interface{}{}
|
||||
}).(map[string]interface{})
|
||||
}
|
||||
|
||||
// Get retrieves and returns specified meta data by `key` from `object`.
|
||||
func Get(object interface{}, key string) *gvar.Var {
|
||||
return gvar.New(Data(object)[key])
|
||||
}
|
||||
78
util/gmeta/gmeta_test.go
Normal file
78
util/gmeta/gmeta_test.go
Normal file
@ -0,0 +1,78 @@
|
||||
// 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 gmeta_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
type A struct {
|
||||
gmeta.Meta `tag:"123" orm:"456"`
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
var (
|
||||
a1 A
|
||||
a2 *A
|
||||
)
|
||||
|
||||
func Benchmark_Data_Struct(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmeta.Data(a1)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Data_Pointer1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmeta.Data(a2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Data_Pointer2(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmeta.Data(&a2)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Data_Get_Struct(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmeta.Get(a1, "tag")
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Data_Get_Pointer1(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmeta.Get(a2, "tag")
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Data_Get_Pointer2(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
gmeta.Get(&a2, "tag")
|
||||
}
|
||||
}
|
||||
|
||||
func TestMeta_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := &A{
|
||||
Id: 100,
|
||||
Name: "john",
|
||||
}
|
||||
t.Assert(len(gmeta.Data(a)), 2)
|
||||
t.Assert(gmeta.Get(a, "tag").String(), "123")
|
||||
t.Assert(gmeta.Get(a, "orm").String(), "456")
|
||||
|
||||
b, err := json.Marshal(a)
|
||||
t.AssertNil(err)
|
||||
t.Assert(b, `{"Id":100,"Name":"john"}`)
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user