mirror of
https://gitee.com/johng/gf
synced 2026-06-07 02:12:11 +08:00
add auto-json support for slice/struct attribute for data inserting of package gdb
This commit is contained in:
@ -431,10 +431,10 @@ func (c *Core) DoInsert(link Link, table string, data interface{}, option int, b
|
||||
if _, ok := data.(apiInterfaces); ok {
|
||||
return c.DB.DoBatchInsert(link, table, data, option, batch...)
|
||||
} else {
|
||||
dataMap = DataToMapDeep(data)
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
}
|
||||
case reflect.Map:
|
||||
dataMap = DataToMapDeep(data)
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported data type:", reflectKind))
|
||||
}
|
||||
@ -526,10 +526,10 @@ func (c *Core) BatchSave(table string, list interface{}, batch ...int) (sql.Resu
|
||||
func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option int, batch ...int) (result sql.Result, err error) {
|
||||
table = c.DB.QuotePrefixTableName(table)
|
||||
var (
|
||||
keys []string
|
||||
values []string
|
||||
params []interface{}
|
||||
listMap List
|
||||
keys []string // Field names.
|
||||
values []string // Value holder string array, like: (?,?,?)
|
||||
params []interface{} // Values that will be committed to underlying database driver.
|
||||
listMap List // The data list that passed from caller.
|
||||
)
|
||||
switch value := list.(type) {
|
||||
case Result:
|
||||
@ -554,10 +554,10 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
case reflect.Slice, reflect.Array:
|
||||
listMap = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
listMap[i] = DataToMapDeep(rv.Index(i).Interface())
|
||||
listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map:
|
||||
listMap = List{DataToMapDeep(value)}
|
||||
listMap = List{ConvertDataForTableRecord(value)}
|
||||
case reflect.Struct:
|
||||
if v, ok := value.(apiInterfaces); ok {
|
||||
var (
|
||||
@ -565,11 +565,11 @@ func (c *Core) DoBatchInsert(link Link, table string, list interface{}, option i
|
||||
list = make(List, len(array))
|
||||
)
|
||||
for i := 0; i < len(array); i++ {
|
||||
list[i] = DataToMapDeep(array[i])
|
||||
list[i] = ConvertDataForTableRecord(array[i])
|
||||
}
|
||||
listMap = list
|
||||
} else {
|
||||
listMap = List{DataToMapDeep(value)}
|
||||
listMap = List{ConvertDataForTableRecord(value)}
|
||||
}
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
|
||||
@ -695,7 +695,7 @@ func (c *Core) DoUpdate(link Link, table string, data interface{}, condition str
|
||||
case reflect.Map, reflect.Struct:
|
||||
var (
|
||||
fields []string
|
||||
dataMap = DataToMapDeep(data)
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
)
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, c.DB.QuoteWord(k)+"=?")
|
||||
|
||||
@ -235,7 +235,7 @@ func (d *DriverOracle) DoInsert(link Link, table string, data interface{}, optio
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
dataMap = DataToMapDeep(data)
|
||||
dataMap = ConvertDataForTableRecord(data)
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported data type:", kind))
|
||||
}
|
||||
@ -355,12 +355,12 @@ func (d *DriverOracle) DoBatchInsert(link Link, table string, list interface{},
|
||||
case reflect.Array:
|
||||
listMap = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
listMap[i] = DataToMapDeep(rv.Index(i).Interface())
|
||||
listMap[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map:
|
||||
fallthrough
|
||||
case reflect.Struct:
|
||||
listMap = List{Map(DataToMapDeep(list))}
|
||||
listMap = List{Map(ConvertDataForTableRecord(list))}
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/utils"
|
||||
"github.com/gogf/gf/os/gtime"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
@ -97,9 +98,52 @@ func GetInsertOperationByOption(option int) string {
|
||||
return operator
|
||||
}
|
||||
|
||||
// DataToMapDeep converts struct object to map type recursively.
|
||||
// ConvertDataForTableRecord is a very important function, which does converting for any data that
|
||||
// will be inserted into table as a record.
|
||||
//
|
||||
// The parameter <obj> should be type of *map/map/*struct/struct.
|
||||
// It supports inherit struct definition for struct.
|
||||
func ConvertDataForTableRecord(value interface{}) map[string]interface{} {
|
||||
var (
|
||||
rvValue reflect.Value
|
||||
rvKind reflect.Kind
|
||||
data = DataToMapDeep(value)
|
||||
)
|
||||
for k, v := range data {
|
||||
rvValue = reflect.ValueOf(v)
|
||||
rvKind = rvValue.Kind()
|
||||
for rvKind == reflect.Ptr {
|
||||
rvValue = rvValue.Elem()
|
||||
rvKind = rvValue.Kind()
|
||||
}
|
||||
switch rvKind {
|
||||
case reflect.Slice, reflect.Array, reflect.Map:
|
||||
// It should ignore the bytes type.
|
||||
if _, ok := v.([]byte); !ok {
|
||||
// Convert the value to JSON.
|
||||
data[k], _ = json.Marshal(v)
|
||||
}
|
||||
case reflect.Struct:
|
||||
switch v.(type) {
|
||||
case time.Time, *time.Time, gtime.Time, *gtime.Time:
|
||||
continue
|
||||
default:
|
||||
// Use string conversion in default.
|
||||
if s, ok := v.(apiString); ok {
|
||||
data[k] = s.String()
|
||||
} else {
|
||||
// Convert the value to JSON.
|
||||
data[k], _ = json.Marshal(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
// DataToMapDeep converts <value> to map type recursively.
|
||||
// The parameter <value> should be type of *map/map/*struct/struct.
|
||||
// It supports inherit struct definition for struct.
|
||||
func DataToMapDeep(value interface{}) map[string]interface{} {
|
||||
if v, ok := value.(apiMapStrAny); ok {
|
||||
return v.MapStrAny()
|
||||
|
||||
@ -68,11 +68,11 @@ func (m *Model) Data(data ...interface{}) *Model {
|
||||
case reflect.Slice, reflect.Array:
|
||||
list := make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
list[i] = DataToMapDeep(rv.Index(i).Interface())
|
||||
list[i] = ConvertDataForTableRecord(rv.Index(i).Interface())
|
||||
}
|
||||
model.data = list
|
||||
case reflect.Map:
|
||||
model.data = DataToMapDeep(data[0])
|
||||
model.data = ConvertDataForTableRecord(data[0])
|
||||
case reflect.Struct:
|
||||
if v, ok := data[0].(apiInterfaces); ok {
|
||||
var (
|
||||
@ -80,11 +80,11 @@ func (m *Model) Data(data ...interface{}) *Model {
|
||||
list = make(List, len(array))
|
||||
)
|
||||
for i := 0; i < len(array); i++ {
|
||||
list[i] = DataToMapDeep(array[i])
|
||||
list[i] = ConvertDataForTableRecord(array[i])
|
||||
}
|
||||
model.data = list
|
||||
} else {
|
||||
model.data = DataToMapDeep(data[0])
|
||||
model.data = ConvertDataForTableRecord(data[0])
|
||||
}
|
||||
default:
|
||||
model.data = data[0]
|
||||
|
||||
@ -59,7 +59,7 @@ func (m *Model) Update(dataAndWhere ...interface{}) (result sql.Result, err erro
|
||||
}
|
||||
switch refKind {
|
||||
case reflect.Map, reflect.Struct:
|
||||
dataMap := DataToMapDeep(m.data)
|
||||
dataMap := ConvertDataForTableRecord(m.data)
|
||||
gutil.MapDelete(dataMap, fieldNameCreate, fieldNameUpdate, fieldNameDelete)
|
||||
if fieldNameUpdate != "" {
|
||||
dataMap[fieldNameUpdate] = gtime.Now().String()
|
||||
|
||||
@ -292,12 +292,12 @@ CREATE TABLE %s (
|
||||
}
|
||||
|
||||
// Fix issue: https://github.com/gogf/gf/issues/819
|
||||
func Test_Func_DataToMapDeep(t *testing.T) {
|
||||
func Test_Func_ConvertDataForTableRecord(t *testing.T) {
|
||||
type Test struct {
|
||||
ResetPasswordTokenAt mysql.NullTime `orm:"reset_password_token_at"`
|
||||
}
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := DataToMapDeep(new(Test))
|
||||
m := ConvertDataForTableRecord(new(Test))
|
||||
t.Assert(len(m), 1)
|
||||
t.AssertNE(m["reset_password_token_at"], nil)
|
||||
t.Assert(m["reset_password_token_at"], new(mysql.NullTime))
|
||||
|
||||
@ -9,6 +9,7 @@ package gdb_test
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -183,6 +184,33 @@ func Test_DB_Insert(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_Insert_WithStructAndSliceAttribute(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Password struct {
|
||||
Salt string `json:"salt"`
|
||||
Pass string `json:"pass"`
|
||||
}
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": &Password{"123", "456"},
|
||||
"nickname": []string{"A", "B", "C"},
|
||||
"create_time": gtime.Now().String(),
|
||||
}
|
||||
_, err := db.Insert(table, data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
one, err := db.GetOne(fmt.Sprintf("SELECT * FROM %s WHERE id=?", table), 1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["create_time"], data["create_time"])
|
||||
t.Assert(one["nickname"], gparser.MustToJson(data["nickname"]))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_DB_InsertIgnore(t *testing.T) {
|
||||
table := createInitTable()
|
||||
defer dropTable(table)
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/encoding/gparser"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"testing"
|
||||
"time"
|
||||
@ -96,6 +97,32 @@ func Test_Model_Insert(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_Insert_WithStructAndSliceAttribute(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type Password struct {
|
||||
Salt string `json:"salt"`
|
||||
Pass string `json:"pass"`
|
||||
}
|
||||
data := g.Map{
|
||||
"id": 1,
|
||||
"passport": "t1",
|
||||
"password": &Password{"123", "456"},
|
||||
"nickname": []string{"A", "B", "C"},
|
||||
"create_time": gtime.Now().String(),
|
||||
}
|
||||
_, err := db.Table(table).Data(data).Insert()
|
||||
t.Assert(err, nil)
|
||||
|
||||
one, err := db.Table(table).One("id", 1)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(one["passport"], data["passport"])
|
||||
t.Assert(one["create_time"], data["create_time"])
|
||||
t.Assert(one["nickname"], gparser.MustToJson(data["nickname"]))
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Model_BatchInsertWithArrayStruct(t *testing.T) {
|
||||
table := createTable()
|
||||
defer dropTable(table)
|
||||
|
||||
Reference in New Issue
Block a user