mirror of
https://gitee.com/johng/gf
synced 2026-06-07 10:22:11 +08:00
merge master
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@ -15,3 +15,5 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
.example/other/
|
||||
main
|
||||
gf
|
||||
@ -7,92 +7,45 @@
|
||||
package gvar
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"reflect"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
)
|
||||
|
||||
// IsNil checks whether <v> is nil.
|
||||
// IsNil checks whether `v` is nil.
|
||||
func (v *Var) IsNil() bool {
|
||||
return v.Val() == nil
|
||||
return utils.IsNil(v.Val())
|
||||
}
|
||||
|
||||
// IsEmpty checks whether <v> is empty.
|
||||
// IsEmpty checks whether `v` is empty.
|
||||
func (v *Var) IsEmpty() bool {
|
||||
return empty.IsEmpty(v.Val())
|
||||
return utils.IsEmpty(v.Val())
|
||||
}
|
||||
|
||||
// IsInt checks whether <v> is type of int.
|
||||
// IsInt checks whether `v` is type of int.
|
||||
func (v *Var) IsInt() bool {
|
||||
switch v.Val().(type) {
|
||||
case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsInt(v.Val())
|
||||
}
|
||||
|
||||
// IsUint checks whether <v> is type of uint.
|
||||
// IsUint checks whether `v` is type of uint.
|
||||
func (v *Var) IsUint() bool {
|
||||
switch v.Val().(type) {
|
||||
case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsUint(v.Val())
|
||||
}
|
||||
|
||||
// IsFloat checks whether <v> is type of float.
|
||||
// IsFloat checks whether `v` is type of float.
|
||||
func (v *Var) IsFloat() bool {
|
||||
switch v.Val().(type) {
|
||||
case float32, *float32, float64, *float64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsFloat(v.Val())
|
||||
}
|
||||
|
||||
// IsSlice checks whether <v> is type of slice.
|
||||
// IsSlice checks whether `v` is type of slice.
|
||||
func (v *Var) IsSlice() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsSlice(v.Val())
|
||||
}
|
||||
|
||||
// IsMap checks whether <v> is type of map.
|
||||
// IsMap checks whether `v` is type of map.
|
||||
func (v *Var) IsMap() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsMap(v.Val())
|
||||
}
|
||||
|
||||
// IsStruct checks whether <v> is type of struct.
|
||||
// IsStruct checks whether `v` is type of struct.
|
||||
func (v *Var) IsStruct() bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(v.Val())
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
return utils.IsStruct(v.Val())
|
||||
}
|
||||
|
||||
@ -41,7 +41,7 @@ func (v *Var) MapDeep(tags ...string) map[string]interface{} {
|
||||
return gconv.MapDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
// MapDeep converts and returns <v> as map[string]string recursively.
|
||||
// MapStrStrDeep converts and returns <v> as map[string]string recursively.
|
||||
func (v *Var) MapStrStrDeep(tags ...string) map[string]string {
|
||||
return gconv.MapStrStrDeep(v.Val(), tags...)
|
||||
}
|
||||
|
||||
@ -353,7 +353,7 @@ func (c *Core) Save(table string, data interface{}, batch ...int) (sql.Result, e
|
||||
return c.Model(table).Data(data).Save()
|
||||
}
|
||||
|
||||
// DoInsert inserts or updates data for given table.
|
||||
// DoInsert inserts or updates data forF given table.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
|
||||
// Eg:
|
||||
@ -510,29 +510,34 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
|
||||
switch kind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
var (
|
||||
fields []string
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
fields []string
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
counterHandler = func(column string, counter Counter) {
|
||||
if counter.Value != 0 {
|
||||
var (
|
||||
column = c.QuoteWord(column)
|
||||
columnRef = c.QuoteWord(counter.Field)
|
||||
columnVal = counter.Value
|
||||
operator = "+"
|
||||
)
|
||||
if columnVal < 0 {
|
||||
operator = "-"
|
||||
columnVal = -columnVal
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s%s?", column, columnRef, operator))
|
||||
params = append(params, columnVal)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
for k, v := range dataMap {
|
||||
switch value := v.(type) {
|
||||
case *Counter:
|
||||
if value.Value != 0 {
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
}
|
||||
counterHandler(k, *value)
|
||||
|
||||
case Counter:
|
||||
if value.Value != 0 {
|
||||
column := k
|
||||
if value.Field != "" {
|
||||
column = c.QuoteWord(value.Field)
|
||||
}
|
||||
fields = append(fields, fmt.Sprintf("%s=%s+?", column, column))
|
||||
params = append(params, value.Value)
|
||||
}
|
||||
counterHandler(k, value)
|
||||
|
||||
default:
|
||||
if s, ok := v.(Raw); ok {
|
||||
fields = append(fields, c.QuoteWord(k)+"="+gconv.String(s))
|
||||
@ -543,6 +548,7 @@ func (c *Core) DoUpdate(ctx context.Context, link Link, table string, data inter
|
||||
}
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
|
||||
default:
|
||||
updates = gconv.String(data)
|
||||
}
|
||||
|
||||
@ -287,3 +287,17 @@ ORDER BY a.id,a.colorder`,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert is not supported in mssql.
|
||||
func (d *DriverMssql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.New(`Save operation is not supported by mssql driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.New(`Replace operation is not supported by mssql driver`)
|
||||
|
||||
default:
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
}
|
||||
|
||||
@ -263,7 +263,27 @@ func (d *DriverOracle) getTableUniqueIndex(table string) (fields map[string]map[
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert inserts or updates data for given table.
|
||||
// This function is usually used for custom interface definition, you do not need call it manually.
|
||||
// The parameter `data` can be type of map/gmap/struct/*struct/[]map/[]struct, etc.
|
||||
// Eg:
|
||||
// Data(g.Map{"uid": 10000, "name":"john"})
|
||||
// Data(g.Slice{g.Map{"uid": 10000, "name":"john"}, g.Map{"uid": 20000, "name":"smith"})
|
||||
//
|
||||
// The parameter `option` values are as follows:
|
||||
// 0: insert: just insert, if there's unique/primary key in the data, it returns error;
|
||||
// 1: replace: if there's unique/primary key in the data, it deletes it from table and inserts a new one;
|
||||
// 2: save: if there's unique/primary key in the data, it updates it or else inserts a new one;
|
||||
// 3: ignore: if there's unique/primary key in the data, it ignores the inserting;
|
||||
func (d *DriverOracle) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.New(`Save operation is not supported by mssql driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.New(`Replace operation is not supported by mssql driver`)
|
||||
}
|
||||
|
||||
var (
|
||||
keys []string
|
||||
values []string
|
||||
|
||||
@ -142,9 +142,18 @@ func (d *DriverPgsql) TableFields(ctx context.Context, table string, schema ...s
|
||||
result Result
|
||||
link, err = d.SlaveLink(useSchema)
|
||||
structureSql = fmt.Sprintf(`
|
||||
SELECT a.attname AS field, t.typname AS type,b.description as comment FROM pg_class c, pg_attribute a
|
||||
LEFT OUTER JOIN pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid,pg_type t
|
||||
WHERE c.relname = '%s' and a.attnum > 0 and a.attrelid = c.oid and a.atttypid = t.oid
|
||||
SELECT a.attname AS field, t.typname AS type,a.attnotnull as null,
|
||||
(case when d.contype is not null then 'pri' else '' end) as key
|
||||
,ic.column_default as default_value,b.description as comment
|
||||
,coalesce(character_maximum_length, numeric_precision, -1) as length
|
||||
,numeric_scale as scale
|
||||
FROM pg_attribute a
|
||||
left join pg_class c on a.attrelid = c.oid
|
||||
left join pg_constraint d on d.conrelid = c.oid and a.attnum = d.conkey[1]
|
||||
left join pg_description b ON a.attrelid=b.objoid AND a.attnum = b.objsubid
|
||||
left join pg_type t ON a.atttypid = t.oid
|
||||
left join information_schema.columns ic on ic.column_name = a.attname and ic.table_name = c.relname
|
||||
WHERE c.relname = '%s' and a.attnum > 0
|
||||
ORDER BY a.attnum`,
|
||||
strings.ToLower(table),
|
||||
)
|
||||
@ -163,6 +172,9 @@ ORDER BY a.attnum`,
|
||||
Index: i,
|
||||
Name: m["field"].String(),
|
||||
Type: m["type"].String(),
|
||||
Null: m["null"].Bool(),
|
||||
Key: m["key"].String(),
|
||||
Default: m["default_value"].Val(),
|
||||
Comment: m["comment"].String(),
|
||||
}
|
||||
}
|
||||
@ -173,3 +185,17 @@ ORDER BY a.attnum`,
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert is not supported in pgsql.
|
||||
func (d *DriverPgsql) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.New(`Save operation is not supported by pgsql driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.New(`Replace operation is not supported by pgsql driver`)
|
||||
|
||||
default:
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
}
|
||||
|
||||
@ -136,3 +136,17 @@ func (d *DriverSqlite) TableFields(ctx context.Context, table string, schema ...
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DoInsert is not supported in sqlite.
|
||||
func (d *DriverSqlite) DoInsert(ctx context.Context, link Link, table string, list List, option DoInsertOption) (result sql.Result, err error) {
|
||||
switch option.InsertOption {
|
||||
case insertOptionSave:
|
||||
return nil, gerror.New(`Save operation is not supported by sqlite driver`)
|
||||
|
||||
case insertOptionReplace:
|
||||
return nil, gerror.New(`Replace operation is not supported by sqlite driver`)
|
||||
|
||||
default:
|
||||
return d.Core.DoInsert(ctx, link, table, list, option)
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,6 +70,31 @@ var (
|
||||
structTagPriority = append([]string{OrmTagForStruct}, gconv.StructTagPriority...)
|
||||
)
|
||||
|
||||
// guessPrimaryTableName parses and returns the primary table name.
|
||||
func (m *Model) guessPrimaryTableName(tableStr string) string {
|
||||
if tableStr == "" {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
guessedTableName = ""
|
||||
array1 = gstr.SplitAndTrim(tableStr, ",")
|
||||
array2 = gstr.SplitAndTrim(array1[0], " ")
|
||||
array3 = gstr.SplitAndTrim(array2[0], ".")
|
||||
)
|
||||
if len(array3) >= 2 {
|
||||
guessedTableName = array3[1]
|
||||
}
|
||||
guessedTableName = array3[0]
|
||||
charL, charR := m.db.GetChars()
|
||||
if charL != "" || charR != "" {
|
||||
guessedTableName = gstr.Trim(guessedTableName, charL+charR)
|
||||
}
|
||||
if !gregex.IsMatchString(regularFieldNameRegPattern, guessedTableName) {
|
||||
return ""
|
||||
}
|
||||
return guessedTableName
|
||||
}
|
||||
|
||||
// getTableNameFromOrmTag retrieves and returns the table name from struct object.
|
||||
func getTableNameFromOrmTag(object interface{}) string {
|
||||
var tableName string
|
||||
@ -239,7 +264,7 @@ func DataToMapDeep(value interface{}) map[string]interface{} {
|
||||
name = ""
|
||||
fieldTag = rtField.Tag
|
||||
for _, tag := range structTagPriority {
|
||||
if s := fieldTag.Get(tag); s != "" && gregex.IsMatchString(regularFieldNameWithoutDotRegPattern, s) {
|
||||
if s := fieldTag.Get(tag); s != "" {
|
||||
name = s
|
||||
break
|
||||
}
|
||||
@ -789,7 +814,9 @@ func formatError(err error, sql string, args ...interface{}) error {
|
||||
func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
index := -1
|
||||
newQuery, _ := gregex.ReplaceStringFunc(
|
||||
`(\?|:v\d+|\$\d+|@p\d+)`, sql, func(s string) string {
|
||||
`(\?|:v\d+|\$\d+|@p\d+)`,
|
||||
sql,
|
||||
func(s string) string {
|
||||
index++
|
||||
if len(args) > index {
|
||||
if args[index] == nil {
|
||||
@ -809,6 +836,7 @@ func FormatSqlWithArgs(sql string, args []interface{}) string {
|
||||
switch kind {
|
||||
case reflect.String, reflect.Map, reflect.Slice, reflect.Array:
|
||||
return `'` + gstr.QuoteMeta(gconv.String(args[index]), `'`) + `'`
|
||||
|
||||
case reflect.Struct:
|
||||
if t, ok := args[index].(time.Time); ok {
|
||||
return `'` + t.Format(`2006-01-02 15:04:05`) + `'`
|
||||
|
||||
@ -206,7 +206,7 @@ func (m *Model) As(as string) *Model {
|
||||
if m.tables != "" {
|
||||
model := m.getModel()
|
||||
split := " JOIN "
|
||||
if gstr.Contains(model.tables, split) {
|
||||
if gstr.ContainsI(model.tables, split) {
|
||||
// For join table.
|
||||
array := gstr.Split(model.tables, split)
|
||||
array[len(array)-1], _ = gregex.ReplaceString(`(.+) ON`, fmt.Sprintf(`$1 AS %s ON`, as), array[len(array)-1])
|
||||
|
||||
@ -90,13 +90,13 @@ func (m *Model) FieldsStr(prefix ...string) string {
|
||||
}
|
||||
|
||||
// GetFieldsStr retrieves and returns all fields from the table, joined with char ','.
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: FieldsStr("u.").
|
||||
// The optional parameter `prefix` specifies the prefix for each field, eg: GetFieldsStr("u.").
|
||||
func (m *Model) GetFieldsStr(prefix ...string) string {
|
||||
prefixStr := ""
|
||||
if len(prefix) > 0 {
|
||||
prefixStr = prefix[0]
|
||||
}
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -136,7 +136,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
|
||||
if len(prefix) > 0 {
|
||||
prefixStr = prefix[0]
|
||||
}
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -164,7 +164,7 @@ func (m *Model) GetFieldsExStr(fields string, prefix ...string) string {
|
||||
|
||||
// HasField determine whether the field exists in the table.
|
||||
func (m *Model) HasField(field string) (bool, error) {
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@ -76,7 +76,7 @@ func (m *Model) getFieldsFiltered() string {
|
||||
panic("function FieldsEx supports only single table operations")
|
||||
}
|
||||
// Filter table fields with fieldEx.
|
||||
tableFields, err := m.TableFields(m.tables)
|
||||
tableFields, err := m.TableFields(m.tablesInit)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -358,11 +358,11 @@ func (m *Model) Scan(pointer interface{}, where ...interface{}) error {
|
||||
// parameter.
|
||||
// See the example or unit testing cases for clear understanding for this function.
|
||||
func (m *Model) ScanList(listPointer interface{}, attributeName string, relation ...string) (err error) {
|
||||
all, err := m.All()
|
||||
result, err := m.All()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ScanList(listPointer, attributeName, relation...)
|
||||
return doScanList(m, result, listPointer, attributeName, relation...)
|
||||
}
|
||||
|
||||
// Count does "SELECT COUNT(x) FROM ..." statement for the model.
|
||||
|
||||
@ -40,7 +40,7 @@ func (m *Model) getSoftFieldNameCreated(table ...string) string {
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
} else {
|
||||
tableName = m.getPrimaryTableName()
|
||||
tableName = m.tablesInit
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.CreatedAt != "" {
|
||||
@ -61,7 +61,7 @@ func (m *Model) getSoftFieldNameUpdated(table ...string) (field string) {
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
} else {
|
||||
tableName = m.getPrimaryTableName()
|
||||
tableName = m.tablesInit
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.UpdatedAt != "" {
|
||||
@ -82,7 +82,7 @@ func (m *Model) getSoftFieldNameDeleted(table ...string) (field string) {
|
||||
if len(table) > 0 {
|
||||
tableName = table[0]
|
||||
} else {
|
||||
tableName = m.getPrimaryTableName()
|
||||
tableName = m.tablesInit
|
||||
}
|
||||
config := m.db.GetConfig()
|
||||
if config.UpdatedAt != "" {
|
||||
@ -170,17 +170,3 @@ func (m *Model) getConditionOfTableStringForSoftDeleting(s string) string {
|
||||
}
|
||||
return fmt.Sprintf(`%s.%s IS NULL`, m.db.GetCore().QuoteWord(table), m.db.GetCore().QuoteWord(field))
|
||||
}
|
||||
|
||||
// getPrimaryTableName parses and returns the primary table name.
|
||||
func (m *Model) getPrimaryTableName() string {
|
||||
if m.tables == "" {
|
||||
return ""
|
||||
}
|
||||
array1 := gstr.SplitAndTrim(m.tables, ",")
|
||||
array2 := gstr.SplitAndTrim(array1[0], " ")
|
||||
array3 := gstr.SplitAndTrim(array2[0], ".")
|
||||
if len(array3) >= 2 {
|
||||
return array3[1]
|
||||
}
|
||||
return array3[0]
|
||||
}
|
||||
|
||||
@ -21,15 +21,12 @@ import (
|
||||
// schema.
|
||||
//
|
||||
// Also see DriverMysql.TableFields.
|
||||
func (m *Model) TableFields(table string, schema ...string) (fields map[string]*TableField, err error) {
|
||||
charL, charR := m.db.GetChars()
|
||||
if charL != "" || charR != "" {
|
||||
table = gstr.Trim(table, charL+charR)
|
||||
func (m *Model) TableFields(tableStr string, schema ...string) (fields map[string]*TableField, err error) {
|
||||
useSchema := m.schema
|
||||
if len(schema) > 0 && schema[0] != "" {
|
||||
useSchema = schema[0]
|
||||
}
|
||||
if !gregex.IsMatchString(regularFieldNameRegPattern, table) {
|
||||
return nil, nil
|
||||
}
|
||||
return m.db.TableFields(m.GetCtx(), table, schema...)
|
||||
return m.db.TableFields(m.GetCtx(), m.guessPrimaryTableName(tableStr), useSchema)
|
||||
}
|
||||
|
||||
// getModel creates and returns a cloned model of current model if `safe` is true, or else it returns
|
||||
@ -47,7 +44,7 @@ func (m *Model) getModel() *Model {
|
||||
// ID -> id
|
||||
// NICK_Name -> nickname
|
||||
func (m *Model) mappingAndFilterToTableFields(fields []string, filter bool) []string {
|
||||
fieldsMap, err := m.TableFields(m.tables)
|
||||
fieldsMap, err := m.TableFields(m.tablesInit)
|
||||
if err != nil || len(fieldsMap) == 0 {
|
||||
return fields
|
||||
}
|
||||
@ -201,7 +198,7 @@ func (m *Model) getLink(master bool) Link {
|
||||
// It parses m.tables to retrieve the primary table name, supporting m.tables like:
|
||||
// "user", "user u", "user as u, user_detail as ud".
|
||||
func (m *Model) getPrimaryKey() string {
|
||||
table := gstr.SplitAndTrim(m.tables, " ")[0]
|
||||
table := gstr.SplitAndTrim(m.tablesInit, " ")[0]
|
||||
tableFields, err := m.TableFields(table)
|
||||
if err != nil {
|
||||
return ""
|
||||
|
||||
@ -39,7 +39,10 @@ func (m *Model) With(objects ...interface{}) *Model {
|
||||
model := m.getModel()
|
||||
for _, object := range objects {
|
||||
if m.tables == "" {
|
||||
m.tables = m.db.GetCore().QuotePrefixTableName(getTableNameFromOrmTag(object))
|
||||
m.tablesInit = m.db.GetCore().QuotePrefixTableName(
|
||||
getTableNameFromOrmTag(object),
|
||||
)
|
||||
m.tables = m.tablesInit
|
||||
return model
|
||||
}
|
||||
model.withArray = append(model.withArray, object)
|
||||
@ -163,6 +166,10 @@ func (m *Model) doWithScanStruct(pointer interface{}) error {
|
||||
// doWithScanStructs handles model association operations feature for struct slice.
|
||||
// Also see doWithScanStruct.
|
||||
func (m *Model) doWithScanStructs(pointer interface{}) error {
|
||||
if v, ok := pointer.(reflect.Value); ok {
|
||||
pointer = v.Interface()
|
||||
}
|
||||
|
||||
var (
|
||||
err error
|
||||
allowedTypeStrArray = make([]string, 0)
|
||||
|
||||
@ -33,7 +33,10 @@ func (r *SqlResult) MustGetInsertId() int64 {
|
||||
return id
|
||||
}
|
||||
|
||||
// see sql.Result.RowsAffected
|
||||
// RowsAffected returns the number of rows affected by an
|
||||
// update, insert, or delete. Not every database or database
|
||||
// driver may support this.
|
||||
// Also See sql.Result.
|
||||
func (r *SqlResult) RowsAffected() (int64, error) {
|
||||
if r.affected > 0 {
|
||||
return r.affected, nil
|
||||
@ -44,7 +47,12 @@ func (r *SqlResult) RowsAffected() (int64, error) {
|
||||
return r.result.RowsAffected()
|
||||
}
|
||||
|
||||
// see sql.Result.LastInsertId
|
||||
// LastInsertId returns the integer generated by the database
|
||||
// in response to a command. Typically this will be from an
|
||||
// "auto increment" column when inserting a new row. Not all
|
||||
// databases support this feature, and the syntax of such
|
||||
// statements varies.
|
||||
// Also See sql.Result.
|
||||
func (r *SqlResult) LastInsertId() (int64, error) {
|
||||
if r.result == nil {
|
||||
return 0, nil
|
||||
|
||||
@ -14,6 +14,11 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
// Interface converts and returns `r` as type of interface{}.
|
||||
func (r Record) Interface() interface{} {
|
||||
return r
|
||||
}
|
||||
|
||||
// Json converts `r` to JSON format content.
|
||||
func (r Record) Json() string {
|
||||
content, _ := gparser.VarToJson(r.Map())
|
||||
|
||||
@ -13,6 +13,11 @@ import (
|
||||
"math"
|
||||
)
|
||||
|
||||
// Interface converts and returns `r` as type of interface{}.
|
||||
func (r Result) Interface() interface{} {
|
||||
return r
|
||||
}
|
||||
|
||||
// IsEmpty checks and returns whether `r` is empty.
|
||||
func (r Result) IsEmpty() bool {
|
||||
return r.Len() == 0
|
||||
|
||||
@ -42,21 +42,21 @@ import (
|
||||
//
|
||||
// See the example or unit testing cases for clear understanding for this function.
|
||||
func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
||||
if r.IsEmpty() {
|
||||
return doScanList(nil, r, listPointer, bindToAttrName, relationKV...)
|
||||
}
|
||||
|
||||
// doScanList converts `result` to struct slice which contains other complex struct attributes recursively.
|
||||
// The parameter `model` is used for recursively scanning purpose, which means, it can scans the attribute struct/structs recursively but
|
||||
// it needs the Model for database accessing.
|
||||
// Note that the parameter `listPointer` should be type of *[]struct/*[]*struct.
|
||||
func doScanList(model *Model, result Result, listPointer interface{}, bindToAttrName string, relationKV ...string) (err error) {
|
||||
if result.IsEmpty() {
|
||||
return nil
|
||||
}
|
||||
// Necessary checks for parameters.
|
||||
if bindToAttrName == "" {
|
||||
return gerror.New(`bindToAttrName should not be empty`)
|
||||
}
|
||||
//if len(relation) > 0 {
|
||||
// if len(relation) < 2 {
|
||||
// return gerror.New(`relation name and key should are both necessary`)
|
||||
// }
|
||||
// if relation[0] == "" || relation[1] == "" {
|
||||
// return gerror.New(`relation name and key should not be empty`)
|
||||
// }
|
||||
//}
|
||||
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(listPointer)
|
||||
@ -67,14 +67,14 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
if reflectKind != reflect.Ptr {
|
||||
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
if reflectKind != reflect.Slice && reflectKind != reflect.Array {
|
||||
return gerror.Newf("parameter should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
return gerror.Newf("listPointer should be type of *[]struct/*[]*struct, but got: %v", reflectKind)
|
||||
}
|
||||
length := len(r)
|
||||
length := len(result)
|
||||
if length == 0 {
|
||||
// The pointed slice is not empty.
|
||||
if reflectValue.Len() > 0 {
|
||||
@ -134,7 +134,7 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// uid:UserId
|
||||
relationResultFieldName = array[0]
|
||||
relationBindToSubAttrName = array[1]
|
||||
if key, _ := gutil.MapPossibleItemByKey(r[0].Map(), relationResultFieldName); key == "" {
|
||||
if key, _ := gutil.MapPossibleItemByKey(result[0].Map(), relationResultFieldName); key == "" {
|
||||
return gerror.Newf(
|
||||
`cannot find possible related table field name "%s" from given relation key "%s"`,
|
||||
relationResultFieldName,
|
||||
@ -147,7 +147,8 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
return gerror.New(`parameter relationKV should be format of "ResultFieldName:BindToAttrName"`)
|
||||
}
|
||||
if relationResultFieldName != "" {
|
||||
relationDataMap = r.MapKeyValue(relationResultFieldName)
|
||||
// Note that the value might be type of slice.
|
||||
relationDataMap = result.MapKeyValue(relationResultFieldName)
|
||||
}
|
||||
if len(relationDataMap) == 0 {
|
||||
return gerror.Newf(`cannot find the relation data map, maybe invalid relation given "%v"`, relationKV)
|
||||
@ -239,12 +240,19 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
if len(relationDataMap) > 0 {
|
||||
relationFromAttrField = relationFromAttrValue.FieldByName(relationBindToSubAttrName)
|
||||
if relationFromAttrField.IsValid() {
|
||||
if err = gconv.Structs(
|
||||
relationDataMap[gconv.String(relationFromAttrField.Interface())],
|
||||
bindToAttrValue.Addr(),
|
||||
); err != nil {
|
||||
results := make(Result, 0)
|
||||
for _, v := range relationDataMap[gconv.String(relationFromAttrField.Interface())].Slice() {
|
||||
results = append(results, v.(Record))
|
||||
}
|
||||
if err = results.Structs(bindToAttrValue.Addr()); err != nil {
|
||||
return err
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStructs(bindToAttrValue.Addr()); err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// May be the attribute does not exist yet.
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
@ -268,24 +276,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
return err
|
||||
if v.IsSlice() {
|
||||
if err = v.Slice()[0].(Record).Struct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = v.Val().(Record).Struct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// May be the attribute does not exist yet.
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
if i >= len(result) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
v := r[i]
|
||||
v := result[i]
|
||||
if v == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(v, element); err != nil {
|
||||
if err = v.Struct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStruct(element); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
@ -300,24 +320,36 @@ func (r Result) ScanList(listPointer interface{}, bindToAttrName string, relatio
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
return err
|
||||
if relationDataItem.IsSlice() {
|
||||
if err = relationDataItem.Slice()[0].(Record).Struct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err = relationDataItem.Val().(Record).Struct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// May be the attribute does not exist yet.
|
||||
return gerror.Newf(`invalid relation specified: "%v"`, relationKV)
|
||||
}
|
||||
} else {
|
||||
if i >= len(r) {
|
||||
if i >= len(result) {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
relationDataItem := r[i]
|
||||
relationDataItem := result[i]
|
||||
if relationDataItem == nil {
|
||||
// There's no relational data.
|
||||
continue
|
||||
}
|
||||
if err = gconv.Struct(relationDataItem, bindToAttrValue); err != nil {
|
||||
if err = relationDataItem.Struct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
// Recursively Scan.
|
||||
if model != nil {
|
||||
if err = model.doWithScanStruct(bindToAttrValue); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,8 +8,11 @@ package gdb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/debug/gdebug"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gmeta"
|
||||
"testing"
|
||||
)
|
||||
@ -782,6 +785,122 @@ PRIMARY KEY (id)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_WithAll_Embedded_WithoutMeta(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 UserDetailBase struct {
|
||||
Uid int `json:"uid"`
|
||||
Address string `json:"address"`
|
||||
}
|
||||
|
||||
type UserDetail struct {
|
||||
UserDetailBase
|
||||
}
|
||||
|
||||
type UserScores struct {
|
||||
Id int `json:"id"`
|
||||
Uid int `json:"uid"`
|
||||
Score int `json:"score"`
|
||||
}
|
||||
|
||||
type User struct {
|
||||
*UserDetail `orm:"with:uid=id"`
|
||||
Id int `json:"id"`
|
||||
Name string `json:"name"`
|
||||
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_AttributeStructAlsoHasWithTag(t *testing.T) {
|
||||
var (
|
||||
tableUser = "user"
|
||||
@ -1189,3 +1308,232 @@ PRIMARY KEY (id)
|
||||
t.Assert(user.UserDetail.UserScores[4].Score, 5)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Table_Relation_With_MultipleDepends1(t *testing.T) {
|
||||
defer func() {
|
||||
dropTable("table_a")
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TableC struct {
|
||||
gmeta.Meta `orm:"table_c"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||
}
|
||||
|
||||
type TableB struct {
|
||||
gmeta.Meta `orm:"table_b"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||
TableC *TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||
}
|
||||
|
||||
type TableA struct {
|
||||
gmeta.Meta `orm:"table_a"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableB *TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
// Struct.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA *TableA
|
||||
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(tableA, nil)
|
||||
t.Assert(tableA.Id, 1)
|
||||
|
||||
t.AssertNE(tableA.TableB, nil)
|
||||
t.AssertNE(tableA.TableB.TableC, nil)
|
||||
t.Assert(tableA.TableB.TableAId, 1)
|
||||
t.Assert(tableA.TableB.TableC.Id, 100)
|
||||
t.Assert(tableA.TableB.TableC.TableBId, 10)
|
||||
})
|
||||
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA []*TableA
|
||||
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(tableA), 2)
|
||||
t.AssertNE(tableA[0].TableB, nil)
|
||||
t.AssertNE(tableA[1].TableB, nil)
|
||||
t.AssertNE(tableA[0].TableB.TableC, nil)
|
||||
t.AssertNE(tableA[1].TableB.TableC, nil)
|
||||
|
||||
t.Assert(tableA[0].Id, 1)
|
||||
t.Assert(tableA[0].TableB.Id, 10)
|
||||
t.Assert(tableA[0].TableB.TableC.Id, 100)
|
||||
|
||||
t.Assert(tableA[1].Id, 2)
|
||||
t.Assert(tableA[1].TableB.Id, 20)
|
||||
t.Assert(tableA[1].TableB.TableC.Id, 300)
|
||||
})
|
||||
}
|
||||
func Test_Table_Relation_With_MultipleDepends2(t *testing.T) {
|
||||
defer func() {
|
||||
dropTable("table_a")
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TableC struct {
|
||||
gmeta.Meta `orm:"table_c"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||
}
|
||||
|
||||
type TableB struct {
|
||||
gmeta.Meta `orm:"table_b"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||
TableC []*TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||
}
|
||||
|
||||
type TableA struct {
|
||||
gmeta.Meta `orm:"table_a"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableB []*TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
// Struct.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA *TableA
|
||||
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(tableA, nil)
|
||||
t.Assert(tableA.Id, 1)
|
||||
|
||||
t.Assert(len(tableA.TableB), 2)
|
||||
t.Assert(tableA.TableB[0].Id, 10)
|
||||
t.Assert(tableA.TableB[1].Id, 30)
|
||||
|
||||
t.Assert(len(tableA.TableB[0].TableC), 2)
|
||||
t.Assert(len(tableA.TableB[1].TableC), 1)
|
||||
t.Assert(tableA.TableB[0].TableC[0].Id, 100)
|
||||
t.Assert(tableA.TableB[0].TableC[0].TableBId, 10)
|
||||
t.Assert(tableA.TableB[0].TableC[1].Id, 200)
|
||||
t.Assert(tableA.TableB[0].TableC[1].TableBId, 10)
|
||||
t.Assert(tableA.TableB[1].TableC[0].Id, 400)
|
||||
t.Assert(tableA.TableB[1].TableC[0].TableBId, 30)
|
||||
})
|
||||
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA []*TableA
|
||||
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(tableA), 2)
|
||||
|
||||
t.Assert(len(tableA[0].TableB), 2)
|
||||
t.Assert(tableA[0].TableB[0].Id, 10)
|
||||
t.Assert(tableA[0].TableB[1].Id, 30)
|
||||
|
||||
t.Assert(len(tableA[0].TableB[0].TableC), 2)
|
||||
t.Assert(len(tableA[0].TableB[1].TableC), 1)
|
||||
t.Assert(tableA[0].TableB[0].TableC[0].Id, 100)
|
||||
t.Assert(tableA[0].TableB[0].TableC[0].TableBId, 10)
|
||||
t.Assert(tableA[0].TableB[0].TableC[1].Id, 200)
|
||||
t.Assert(tableA[0].TableB[0].TableC[1].TableBId, 10)
|
||||
t.Assert(tableA[0].TableB[1].TableC[0].Id, 400)
|
||||
t.Assert(tableA[0].TableB[1].TableC[0].TableBId, 30)
|
||||
|
||||
t.Assert(tableA[1].TableB[0].TableC[0].Id, 300)
|
||||
t.Assert(tableA[1].TableB[0].TableC[0].TableBId, 20)
|
||||
|
||||
t.Assert(tableA[1].TableB[1].Id, 40)
|
||||
t.Assert(tableA[1].TableB[1].TableAId, 2)
|
||||
t.Assert(tableA[1].TableB[1].TableC, nil)
|
||||
})
|
||||
}
|
||||
func Test_Table_Relation_With_MultipleDepends_Embedded(t *testing.T) {
|
||||
defer func() {
|
||||
dropTable("table_a")
|
||||
dropTable("table_b")
|
||||
dropTable("table_c")
|
||||
}()
|
||||
for _, v := range gstr.SplitAndTrim(gfile.GetContents(gdebug.TestDataPath("with_multiple_depends.sql")), ";") {
|
||||
if _, err := db.Exec(v); err != nil {
|
||||
gtest.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
type TableC struct {
|
||||
gmeta.Meta `orm:"table_c"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableBId int `orm:"table_b_id" json:"table_b_id"`
|
||||
}
|
||||
|
||||
type TableB struct {
|
||||
gmeta.Meta `orm:"table_b"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
TableAId int `orm:"table_a_id" json:"table_a_id"`
|
||||
*TableC `orm:"with:table_b_id=id" json:"table_c"`
|
||||
}
|
||||
|
||||
type TableA struct {
|
||||
gmeta.Meta `orm:"table_a"`
|
||||
Id int `orm:"id,primary" json:"id"`
|
||||
*TableB `orm:"with:table_a_id=id" json:"table_b"`
|
||||
}
|
||||
|
||||
db.SetDebug(true)
|
||||
defer db.SetDebug(false)
|
||||
|
||||
// Struct.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA *TableA
|
||||
err := db.Model("table_a").WithAll().Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.AssertNE(tableA, nil)
|
||||
t.Assert(tableA.Id, 1)
|
||||
|
||||
t.AssertNE(tableA.TableB, nil)
|
||||
t.AssertNE(tableA.TableB.TableC, nil)
|
||||
t.Assert(tableA.TableB.TableAId, 1)
|
||||
t.Assert(tableA.TableB.TableC.Id, 100)
|
||||
t.Assert(tableA.TableB.TableC.TableBId, 10)
|
||||
})
|
||||
|
||||
// Structs
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var tableA []*TableA
|
||||
err := db.Model("table_a").WithAll().OrderAsc("id").Scan(&tableA)
|
||||
//g.Dump(tableA)
|
||||
t.AssertNil(err)
|
||||
t.Assert(len(tableA), 2)
|
||||
t.AssertNE(tableA[0].TableB, nil)
|
||||
t.AssertNE(tableA[1].TableB, nil)
|
||||
t.AssertNE(tableA[0].TableB.TableC, nil)
|
||||
t.AssertNE(tableA[1].TableB.TableC, nil)
|
||||
|
||||
t.Assert(tableA[0].Id, 1)
|
||||
t.Assert(tableA[0].TableB.Id, 10)
|
||||
t.Assert(tableA[0].TableB.TableC.Id, 100)
|
||||
|
||||
t.Assert(tableA[1].Id, 2)
|
||||
t.Assert(tableA[1].TableB.Id, 20)
|
||||
t.Assert(tableA[1].TableB.TableC.Id, 300)
|
||||
})
|
||||
}
|
||||
|
||||
@ -1436,7 +1436,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
gdbCounter := &gdb.Counter{
|
||||
Field: "views",
|
||||
Field: "id",
|
||||
Value: 1,
|
||||
}
|
||||
updateData := g.Map{
|
||||
@ -1449,7 +1449,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
one, err := db.Model(tableName).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 2)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
@ -1468,7 +1468,7 @@ func Test_DB_UpdateCounter(t *testing.T) {
|
||||
one, err := db.Model(tableName).Where("id", 1).One()
|
||||
t.AssertNil(err)
|
||||
t.Assert(one["id"].Int(), 1)
|
||||
t.Assert(one["views"].Int(), 0)
|
||||
t.Assert(one["views"].Int(), 1)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
33
database/gdb/testdata/with_multiple_depends.sql
vendored
Normal file
33
database/gdb/testdata/with_multiple_depends.sql
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
|
||||
CREATE TABLE `table_a` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`alias` varchar(255) NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
INSERT INTO `table_a` VALUES (1, 'table_a_test1');
|
||||
INSERT INTO `table_a` VALUES (2, 'table_a_test2');
|
||||
|
||||
CREATE TABLE `table_b` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`table_a_id` int(11) NOT NULL,
|
||||
`alias` varchar(255) NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
INSERT INTO `table_b` VALUES (10, 1, 'table_b_test1');
|
||||
INSERT INTO `table_b` VALUES (20, 2, 'table_b_test2');
|
||||
INSERT INTO `table_b` VALUES (30, 1, 'table_b_test3');
|
||||
INSERT INTO `table_b` VALUES (40, 2, 'table_b_test4');
|
||||
|
||||
CREATE TABLE `table_c` (
|
||||
`id` int(11) NOT NULL AUTO_INCREMENT,
|
||||
`table_b_id` int(11) NOT NULL,
|
||||
`alias` varchar(255) NULL DEFAULT '',
|
||||
PRIMARY KEY (`id`) USING BTREE
|
||||
) ENGINE = InnoDB;
|
||||
|
||||
INSERT INTO `table_c` VALUES (100, 10, 'table_c_test1');
|
||||
INSERT INTO `table_c` VALUES (200, 10, 'table_c_test2');
|
||||
INSERT INTO `table_c` VALUES (300, 20, 'table_c_test3');
|
||||
INSERT INTO `table_c` VALUES (400, 30, 'table_c_test4');
|
||||
@ -8,6 +8,7 @@
|
||||
package gjson
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -37,11 +38,23 @@ type Options struct {
|
||||
StrNumber bool // StrNumber causes the Decoder to unmarshal a number into an interface{} as a string instead of as a float64.
|
||||
}
|
||||
|
||||
// apiInterface is used for type assert api for Interface().
|
||||
type apiInterface interface {
|
||||
Interface() interface{}
|
||||
}
|
||||
|
||||
// setValue sets <value> to <j> by <pattern>.
|
||||
// Note:
|
||||
// 1. If value is nil and removed is true, means deleting this value;
|
||||
// 2. It's quite complicated in hierarchical data search, node creating and data assignment;
|
||||
func (j *Json) setValue(pattern string, value interface{}, removed bool) error {
|
||||
if value != nil {
|
||||
if utils.IsStruct(value) {
|
||||
if v, ok := value.(apiInterface); ok {
|
||||
value = v.Interface()
|
||||
}
|
||||
}
|
||||
}
|
||||
array := strings.Split(pattern, string(j.c))
|
||||
length := len(array)
|
||||
value = j.convertValue(value)
|
||||
|
||||
@ -18,7 +18,13 @@ import (
|
||||
)
|
||||
|
||||
// Value returns the json value.
|
||||
// Deprecated, use Interface instead.
|
||||
func (j *Json) Value() interface{} {
|
||||
return j.Interface()
|
||||
}
|
||||
|
||||
// Interface returns the json value.
|
||||
func (j *Json) Interface() interface{} {
|
||||
j.mu.RLock()
|
||||
defer j.mu.RUnlock()
|
||||
return *(j.p)
|
||||
|
||||
@ -485,3 +485,17 @@ func TestJson_IsNil(t *testing.T) {
|
||||
t.Assert(j.IsNil(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestJson_Set_With_Struct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
v := gjson.New(g.Map{
|
||||
"user1": g.Map{"name": "user1"},
|
||||
"user2": g.Map{"name": "user2"},
|
||||
"user3": g.Map{"name": "user3"},
|
||||
})
|
||||
user1 := v.GetJson("user1")
|
||||
user1.Set("id", 111)
|
||||
v.Set("user1", user1)
|
||||
t.Assert(v.Get("user1.id"), 111)
|
||||
})
|
||||
}
|
||||
|
||||
@ -71,8 +71,9 @@ func doPrint(ctx context.Context, content string, stack bool) {
|
||||
buffer.WriteString(now())
|
||||
buffer.WriteString(" [INTE] ")
|
||||
buffer.WriteString(file())
|
||||
buffer.WriteString(" ")
|
||||
if s := traceIdStr(ctx); s != "" {
|
||||
buffer.WriteString(" " + s)
|
||||
buffer.WriteString(s + " ")
|
||||
}
|
||||
buffer.WriteString(content)
|
||||
buffer.WriteString("\n")
|
||||
|
||||
98
internal/utils/utils_is.go
Normal file
98
internal/utils/utils_is.go
Normal file
@ -0,0 +1,98 @@
|
||||
// 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 utils
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// IsNil checks whether `value` is nil.
|
||||
func IsNil(value interface{}) bool {
|
||||
return value == nil
|
||||
}
|
||||
|
||||
// IsEmpty checks whether `value` is empty.
|
||||
func IsEmpty(value interface{}) bool {
|
||||
return empty.IsEmpty(value)
|
||||
}
|
||||
|
||||
// IsInt checks whether `value` is type of int.
|
||||
func IsInt(value interface{}) bool {
|
||||
switch value.(type) {
|
||||
case int, *int, int8, *int8, int16, *int16, int32, *int32, int64, *int64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsUint checks whether `value` is type of uint.
|
||||
func IsUint(value interface{}) bool {
|
||||
switch value.(type) {
|
||||
case uint, *uint, uint8, *uint8, uint16, *uint16, uint32, *uint32, uint64, *uint64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsFloat checks whether `value` is type of float.
|
||||
func IsFloat(value interface{}) bool {
|
||||
switch value.(type) {
|
||||
case float32, *float32, float64, *float64:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsSlice checks whether `value` is type of slice.
|
||||
func IsSlice(value interface{}) bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Slice, reflect.Array:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsMap checks whether `value` is type of map.
|
||||
func IsMap(value interface{}) bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Map:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// IsStruct checks whether `value` is type of struct.
|
||||
func IsStruct(value interface{}) bool {
|
||||
var (
|
||||
reflectValue = reflect.ValueOf(value)
|
||||
reflectKind = reflectValue.Kind()
|
||||
)
|
||||
for reflectKind == reflect.Ptr {
|
||||
reflectValue = reflectValue.Elem()
|
||||
reflectKind = reflectValue.Kind()
|
||||
}
|
||||
switch reflectKind {
|
||||
case reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
171
internal/utils/utils_z_is_test.go
Normal file
171
internal/utils/utils_z_is_test.go
Normal file
@ -0,0 +1,171 @@
|
||||
// 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 utils_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVar_IsNil(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsNil(0), false)
|
||||
t.Assert(utils.IsNil(nil), true)
|
||||
t.Assert(utils.IsNil(g.Map{}), false)
|
||||
t.Assert(utils.IsNil(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsNil(1), false)
|
||||
t.Assert(utils.IsNil(0.1), false)
|
||||
t.Assert(utils.IsNil(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsNil(g.Slice{0}), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsEmpty(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsEmpty(0), true)
|
||||
t.Assert(utils.IsEmpty(nil), true)
|
||||
t.Assert(utils.IsEmpty(g.Map{}), true)
|
||||
t.Assert(utils.IsEmpty(g.Slice{}), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsEmpty(1), false)
|
||||
t.Assert(utils.IsEmpty(0.1), false)
|
||||
t.Assert(utils.IsEmpty(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsEmpty(g.Slice{0}), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsInt(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsInt(0), true)
|
||||
t.Assert(utils.IsInt(nil), false)
|
||||
t.Assert(utils.IsInt(g.Map{}), false)
|
||||
t.Assert(utils.IsInt(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsInt(1), true)
|
||||
t.Assert(utils.IsInt(-1), true)
|
||||
t.Assert(utils.IsInt(0.1), false)
|
||||
t.Assert(utils.IsInt(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsInt(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsInt(int8(1)), true)
|
||||
t.Assert(utils.IsInt(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsUint(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsUint(0), false)
|
||||
t.Assert(utils.IsUint(nil), false)
|
||||
t.Assert(utils.IsUint(g.Map{}), false)
|
||||
t.Assert(utils.IsUint(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsUint(1), false)
|
||||
t.Assert(utils.IsUint(-1), false)
|
||||
t.Assert(utils.IsUint(0.1), false)
|
||||
t.Assert(utils.IsUint(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsUint(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsUint(int8(1)), false)
|
||||
t.Assert(utils.IsUint(uint8(1)), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsFloat(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsFloat(0), false)
|
||||
t.Assert(utils.IsFloat(nil), false)
|
||||
t.Assert(utils.IsFloat(g.Map{}), false)
|
||||
t.Assert(utils.IsFloat(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsFloat(1), false)
|
||||
t.Assert(utils.IsFloat(-1), false)
|
||||
t.Assert(utils.IsFloat(0.1), true)
|
||||
t.Assert(utils.IsFloat(float64(1)), true)
|
||||
t.Assert(utils.IsFloat(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsFloat(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsFloat(int8(1)), false)
|
||||
t.Assert(utils.IsFloat(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsSlice(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsSlice(0), false)
|
||||
t.Assert(utils.IsSlice(nil), false)
|
||||
t.Assert(utils.IsSlice(g.Map{}), false)
|
||||
t.Assert(utils.IsSlice(g.Slice{}), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsSlice(1), false)
|
||||
t.Assert(utils.IsSlice(-1), false)
|
||||
t.Assert(utils.IsSlice(0.1), false)
|
||||
t.Assert(utils.IsSlice(float64(1)), false)
|
||||
t.Assert(utils.IsSlice(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsSlice(g.Slice{0}), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsSlice(int8(1)), false)
|
||||
t.Assert(utils.IsSlice(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsMap(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsMap(0), false)
|
||||
t.Assert(utils.IsMap(nil), false)
|
||||
t.Assert(utils.IsMap(g.Map{}), true)
|
||||
t.Assert(utils.IsMap(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsMap(1), false)
|
||||
t.Assert(utils.IsMap(-1), false)
|
||||
t.Assert(utils.IsMap(0.1), false)
|
||||
t.Assert(utils.IsMap(float64(1)), false)
|
||||
t.Assert(utils.IsMap(g.Map{"k": "v"}), true)
|
||||
t.Assert(utils.IsMap(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsMap(int8(1)), false)
|
||||
t.Assert(utils.IsMap(uint8(1)), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar_IsStruct(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsStruct(0), false)
|
||||
t.Assert(utils.IsStruct(nil), false)
|
||||
t.Assert(utils.IsStruct(g.Map{}), false)
|
||||
t.Assert(utils.IsStruct(g.Slice{}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
t.Assert(utils.IsStruct(1), false)
|
||||
t.Assert(utils.IsStruct(-1), false)
|
||||
t.Assert(utils.IsStruct(0.1), false)
|
||||
t.Assert(utils.IsStruct(float64(1)), false)
|
||||
t.Assert(utils.IsStruct(g.Map{"k": "v"}), false)
|
||||
t.Assert(utils.IsStruct(g.Slice{0}), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a := &struct {
|
||||
}{}
|
||||
t.Assert(utils.IsStruct(a), true)
|
||||
t.Assert(utils.IsStruct(*a), true)
|
||||
t.Assert(utils.IsStruct(&a), true)
|
||||
})
|
||||
}
|
||||
@ -10,6 +10,7 @@ package ghttp
|
||||
import (
|
||||
"github.com/gogf/gf/os/gcfg"
|
||||
"github.com/gogf/gf/os/gview"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/gmode"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
@ -62,7 +63,7 @@ func (r *Response) ParseTpl(tpl string, params ...gview.Params) (string, error)
|
||||
return r.Request.GetView().Parse(r.Request.Context(), tpl, r.buildInVars(params...))
|
||||
}
|
||||
|
||||
// ParseDefault parses the default template file with params.
|
||||
// ParseTplDefault parses the default template file with params.
|
||||
func (r *Response) ParseTplDefault(params ...gview.Params) (string, error) {
|
||||
return r.Request.GetView().ParseDefault(r.Request.Context(), r.buildInVars(params...))
|
||||
}
|
||||
@ -81,17 +82,18 @@ func (r *Response) buildInVars(params ...map[string]interface{}) map[string]inte
|
||||
gutil.MapMerge(m, params[0])
|
||||
}
|
||||
// Retrieve custom template variables from request object.
|
||||
sessionMap := gconv.MapDeep(r.Request.Session.Map())
|
||||
gutil.MapMerge(m, map[string]interface{}{
|
||||
"Form": r.Request.GetFormMap(),
|
||||
"Query": r.Request.GetQueryMap(),
|
||||
"Request": r.Request.GetMap(),
|
||||
"Cookie": r.Request.Cookie.Map(),
|
||||
"Session": r.Request.Session.Map(),
|
||||
"Session": sessionMap,
|
||||
})
|
||||
// Note that it should assign no Config variable to template
|
||||
// if there's no configuration file.
|
||||
if c := gcfg.Instance(); c.Available() {
|
||||
m["Config"] = c.GetMap(".")
|
||||
m["Config"] = c.Map()
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import (
|
||||
"context"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/container/gvar"
|
||||
"go.opentelemetry.io/otel/attribute"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"go.opentelemetry.io/otel/baggage"
|
||||
)
|
||||
|
||||
@ -37,39 +37,37 @@ func (b *Baggage) Ctx() context.Context {
|
||||
// SetValue is a convenient function for adding one key-value pair to baggage.
|
||||
// Note that it uses attribute.Any to set the key-value pair.
|
||||
func (b *Baggage) SetValue(key string, value interface{}) context.Context {
|
||||
b.ctx = baggage.ContextWithValues(b.ctx, attribute.Any(key, value))
|
||||
member, _ := baggage.NewMember(key, gconv.String(value))
|
||||
bag, _ := baggage.New(member)
|
||||
b.ctx = baggage.ContextWithBaggage(b.ctx, bag)
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
// SetMap is a convenient function for adding map key-value pairs to baggage.
|
||||
// Note that it uses attribute.Any to set the key-value pair.
|
||||
func (b *Baggage) SetMap(data map[string]interface{}) context.Context {
|
||||
pairs := make([]attribute.KeyValue, 0)
|
||||
members := make([]baggage.Member, 0)
|
||||
for k, v := range data {
|
||||
pairs = append(pairs, attribute.Any(k, v))
|
||||
member, _ := baggage.NewMember(k, gconv.String(v))
|
||||
members = append(members, member)
|
||||
}
|
||||
b.ctx = baggage.ContextWithValues(b.ctx, pairs...)
|
||||
bag, _ := baggage.New(members...)
|
||||
b.ctx = baggage.ContextWithBaggage(b.ctx, bag)
|
||||
return b.ctx
|
||||
}
|
||||
|
||||
// GetMap retrieves and returns the baggage values as map.
|
||||
func (b *Baggage) GetMap() *gmap.StrAnyMap {
|
||||
m := gmap.NewStrAnyMap()
|
||||
set := baggage.Set(b.ctx)
|
||||
if length := set.Len(); length > 0 {
|
||||
if length == 0 {
|
||||
return m
|
||||
}
|
||||
inter := set.Iter()
|
||||
for inter.Next() {
|
||||
m.Set(string(inter.Label().Key), inter.Label().Value.AsInterface())
|
||||
}
|
||||
members := baggage.FromContext(b.ctx).Members()
|
||||
for i := range members {
|
||||
m.Set(members[i].Key(), members[i].Value())
|
||||
}
|
||||
return m
|
||||
}
|
||||
|
||||
// GetVar retrieves value and returns a *gvar.Var for specified key from baggage.
|
||||
func (b *Baggage) GetVar(key string) *gvar.Var {
|
||||
value := baggage.Value(b.ctx, attribute.Key(key))
|
||||
return gvar.New(value.AsInterface())
|
||||
value := baggage.FromContext(b.ctx).Member(key).Value()
|
||||
return gvar.New(value)
|
||||
}
|
||||
|
||||
@ -16,7 +16,7 @@ type Span struct {
|
||||
}
|
||||
|
||||
// NewSpan creates a span using default tracer.
|
||||
func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanOption) (context.Context, *Span) {
|
||||
func NewSpan(ctx context.Context, spanName string, opts ...trace.SpanStartOption) (context.Context, *Span) {
|
||||
ctx, span := NewTracer().Start(ctx, spanName, opts...)
|
||||
return ctx, &Span{
|
||||
Span: span,
|
||||
|
||||
@ -57,7 +57,7 @@ func TestNewCarrier(t *testing.T) {
|
||||
t.Assert(carrier1.String(), `{"traceparent":"00-4bf92f3577b34da6a3ce929d0e0e4736-0000000000000002-01","tracestate":""}`)
|
||||
|
||||
ctx = otel.GetTextMapPropagator().Extract(ctx, carrier1)
|
||||
gotSc := trace.RemoteSpanContextFromContext(ctx)
|
||||
gotSc := trace.SpanContextFromContext(ctx)
|
||||
t.Assert(gotSc.TraceID().String(), traceID.String())
|
||||
t.Assert(gotSc.SpanID().String(), "0000000000000002")
|
||||
})
|
||||
|
||||
@ -96,8 +96,8 @@ func New() (*Watcher, error) {
|
||||
return w, nil
|
||||
}
|
||||
|
||||
// Add monitors <path> using default watcher with callback function <callbackFunc>.
|
||||
// The optional parameter <recursive> specifies whether monitoring the <path> recursively, which is true in default.
|
||||
// Add monitors `path` using default watcher with callback function `callbackFunc`.
|
||||
// The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
|
||||
func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
|
||||
w, err := getDefaultWatcher()
|
||||
if err != nil {
|
||||
@ -106,11 +106,11 @@ func Add(path string, callbackFunc func(event *Event), recursive ...bool) (callb
|
||||
return w.Add(path, callbackFunc, recursive...)
|
||||
}
|
||||
|
||||
// AddOnce monitors <path> using default watcher with callback function <callbackFunc> only once using unique name <name>.
|
||||
// If AddOnce is called multiple times with the same <name> parameter, <path> is only added to monitor once. It returns error
|
||||
// if it's called twice with the same <name>.
|
||||
// AddOnce monitors `path` using default watcher with callback function `callbackFunc` only once using unique name `name`.
|
||||
// If AddOnce is called multiple times with the same `name` parameter, `path` is only added to monitor once. It returns error
|
||||
// if it's called twice with the same `name`.
|
||||
//
|
||||
// The optional parameter <recursive> specifies whether monitoring the <path> recursively, which is true in default.
|
||||
// The optional parameter `recursive` specifies whether monitoring the `path` recursively, which is true in default.
|
||||
func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
|
||||
w, err := getDefaultWatcher()
|
||||
if err != nil {
|
||||
@ -119,7 +119,7 @@ func AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bo
|
||||
return w.AddOnce(name, path, callbackFunc, recursive...)
|
||||
}
|
||||
|
||||
// Remove removes all monitoring callbacks of given <path> from watcher recursively.
|
||||
// Remove removes all monitoring callbacks of given `path` from watcher recursively.
|
||||
func Remove(path string) error {
|
||||
w, err := getDefaultWatcher()
|
||||
if err != nil {
|
||||
|
||||
@ -24,7 +24,7 @@ func fileDir(path string) string {
|
||||
return filepath.Dir(path)
|
||||
}
|
||||
|
||||
// fileRealPath converts the given <path> to its absolute path
|
||||
// fileRealPath converts the given `path` to its absolute path
|
||||
// and checks if the file path exists.
|
||||
// If the file does not exist, return an empty string.
|
||||
func fileRealPath(path string) string {
|
||||
@ -38,7 +38,7 @@ func fileRealPath(path string) string {
|
||||
return p
|
||||
}
|
||||
|
||||
// fileExists checks whether given <path> exist.
|
||||
// fileExists checks whether given `path` exist.
|
||||
func fileExists(path string) bool {
|
||||
if stat, err := os.Stat(path); stat != nil && !os.IsNotExist(err) {
|
||||
return true
|
||||
@ -46,7 +46,7 @@ func fileExists(path string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// fileIsDir checks whether given <path> a directory.
|
||||
// fileIsDir checks whether given `path` a directory.
|
||||
func fileIsDir(path string) bool {
|
||||
s, err := os.Stat(path)
|
||||
if err != nil {
|
||||
@ -55,7 +55,7 @@ func fileIsDir(path string) bool {
|
||||
return s.IsDir()
|
||||
}
|
||||
|
||||
// fileAllDirs returns all sub-folders including itself of given <path> recursively.
|
||||
// fileAllDirs returns all sub-folders including itself of given `path` recursively.
|
||||
func fileAllDirs(path string) (list []string) {
|
||||
list = []string{path}
|
||||
file, err := os.Open(path)
|
||||
@ -78,8 +78,8 @@ func fileAllDirs(path string) (list []string) {
|
||||
return
|
||||
}
|
||||
|
||||
// fileScanDir returns all sub-files with absolute paths of given <path>,
|
||||
// It scans directory recursively if given parameter <recursive> is true.
|
||||
// fileScanDir returns all sub-files with absolute paths of given `path`,
|
||||
// It scans directory recursively if given parameter `recursive` is true.
|
||||
func fileScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
|
||||
list, err := doFileScanDir(path, pattern, recursive...)
|
||||
if err != nil {
|
||||
@ -94,10 +94,10 @@ func fileScanDir(path string, pattern string, recursive ...bool) ([]string, erro
|
||||
// doFileScanDir is an internal method which scans directory
|
||||
// and returns the absolute path list of files that are not sorted.
|
||||
//
|
||||
// The pattern parameter <pattern> supports multiple file name patterns,
|
||||
// The pattern parameter `pattern` supports multiple file name patterns,
|
||||
// using the ',' symbol to separate multiple patterns.
|
||||
//
|
||||
// It scans directory recursively if given parameter <recursive> is true.
|
||||
// It scans directory recursively if given parameter `recursive` is true.
|
||||
func doFileScanDir(path string, pattern string, recursive ...bool) ([]string, error) {
|
||||
list := ([]string)(nil)
|
||||
file, err := os.Open(path)
|
||||
|
||||
@ -14,19 +14,20 @@ import (
|
||||
"github.com/gogf/gf/container/glist"
|
||||
)
|
||||
|
||||
// Add monitors <path> with callback function <callbackFunc> to the watcher.
|
||||
// The optional parameter <recursive> specifies whether monitoring the <path> recursively,
|
||||
// Add monitors `path` with callback function `callbackFunc` to the watcher.
|
||||
// The optional parameter `recursive` specifies whether monitoring the `path` recursively,
|
||||
// which is true in default.
|
||||
func (w *Watcher) Add(path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
|
||||
return w.AddOnce("", path, callbackFunc, recursive...)
|
||||
}
|
||||
|
||||
// AddOnce monitors <path> with callback function <callbackFunc> only once using unique name
|
||||
// <name> to the watcher. If AddOnce is called multiple times with the same <name> parameter,
|
||||
// <path> is only added to monitor once.
|
||||
// It returns error if it's called twice with the same <name>.
|
||||
// AddOnce monitors `path` with callback function `callbackFunc` only once using unique name
|
||||
// `name` to the watcher. If AddOnce is called multiple times with the same `name` parameter,
|
||||
// `path` is only added to monitor once.
|
||||
//
|
||||
// The optional parameter <recursive> specifies whether monitoring the <path> recursively,
|
||||
// It returns error if it's called twice with the same `name`.
|
||||
//
|
||||
// The optional parameter `recursive` specifies whether monitoring the `path` recursively,
|
||||
// which is true in default.
|
||||
func (w *Watcher) AddOnce(name, path string, callbackFunc func(event *Event), recursive ...bool) (callback *Callback, err error) {
|
||||
w.nameSet.AddIfNotExistFuncLock(name, func() bool {
|
||||
@ -99,8 +100,6 @@ func (w *Watcher) addWithCallbackFunc(name, path string, callbackFunc func(event
|
||||
}
|
||||
// Add the callback to global callback map.
|
||||
callbackIdMap.Set(callback.Id, callback)
|
||||
|
||||
//intlog.Print("addWithCallbackFunc", name, path, callback.recursive)
|
||||
return
|
||||
}
|
||||
|
||||
@ -113,7 +112,7 @@ func (w *Watcher) Close() {
|
||||
close(w.closeChan)
|
||||
}
|
||||
|
||||
// Remove removes monitor and all callbacks associated with the <path> recursively.
|
||||
// Remove removes monitor and all callbacks associated with the `path` recursively.
|
||||
func (w *Watcher) Remove(path string) error {
|
||||
// Firstly remove the callbacks of the path.
|
||||
if r := w.callbacks.Remove(path); r != nil {
|
||||
|
||||
@ -134,7 +134,7 @@ func (w *Watcher) eventLoop() {
|
||||
}()
|
||||
}
|
||||
|
||||
// getCallbacks searches and returns all callbacks with given <path>.
|
||||
// getCallbacks searches and returns all callbacks with given `path`.
|
||||
// It also searches its parents for callbacks if they're recursive.
|
||||
func (w *Watcher) getCallbacks(path string) (callbacks []*Callback) {
|
||||
// Firstly add the callbacks of itself.
|
||||
|
||||
Reference in New Issue
Block a user