From ee89a06b3ef1f9f38d0e1d1bf565672811b7b0d0 Mon Sep 17 00:00:00 2001 From: John Date: Fri, 30 Aug 2019 20:29:12 +0800 Subject: [PATCH] improve model feature --- .example/frame/main/model/config.toml | 4 + .example/frame/main/model/model1.go | 15 ++++ .example/frame/mvc/model/test/init.go | 10 +++ .example/frame/mvc/model/test/user.go | 81 ++++++++++++++++++++ .example/net/ghttp/server/admin/admin.go | 17 ++++ .example/os/gtimer/gtimer2.go | 4 +- .example/other/test.go | 4 +- database/gdb/gdb_func.go | 40 +++++++++- database/gdb/gdb_model.go | 8 +- encoding/gjson/gjson_api_new_load.go | 21 +++-- net/ghttp/ghttp_server_service_controller.go | 2 +- net/ghttp/ghttp_server_service_object.go | 2 +- os/gfile/gfile_source.go | 28 ++++--- os/gres/gres.go | 13 ---- os/gres/gres_func.go | 13 ++++ text/gstr/gstr.go | 20 +++-- text/gstr/gstr_z_unit_basic_test.go | 3 + 17 files changed, 232 insertions(+), 53 deletions(-) create mode 100644 .example/frame/main/model/config.toml create mode 100644 .example/frame/main/model/model1.go create mode 100644 .example/frame/mvc/model/test/init.go create mode 100644 .example/frame/mvc/model/test/user.go create mode 100644 .example/net/ghttp/server/admin/admin.go diff --git a/.example/frame/main/model/config.toml b/.example/frame/main/model/config.toml new file mode 100644 index 000000000..931c6287b --- /dev/null +++ b/.example/frame/main/model/config.toml @@ -0,0 +1,4 @@ + +# MySQL数据库配置 +[database] + link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test" diff --git a/.example/frame/main/model/model1.go b/.example/frame/main/model/model1.go new file mode 100644 index 000000000..651976d41 --- /dev/null +++ b/.example/frame/main/model/model1.go @@ -0,0 +1,15 @@ +package main + +import ( + "github.com/gogf/gf/.example/frame/mvc/model/test" + "github.com/gogf/gf/frame/g" +) + +func main() { + g.DB().SetDebug(true) + user, err := test.ModelUser().One() + g.Dump(err) + g.Dump(user) + user.Password = "1" + g.Dump(user.Update()) +} diff --git a/.example/frame/mvc/model/test/init.go b/.example/frame/mvc/model/test/init.go new file mode 100644 index 000000000..f40e33ecd --- /dev/null +++ b/.example/frame/mvc/model/test/init.go @@ -0,0 +1,10 @@ +package test + +import ( + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/frame/g" +) + +func DB() gdb.DB { + return g.DB() +} diff --git a/.example/frame/mvc/model/test/user.go b/.example/frame/mvc/model/test/user.go new file mode 100644 index 000000000..c11cb6746 --- /dev/null +++ b/.example/frame/mvc/model/test/user.go @@ -0,0 +1,81 @@ +package test + +import ( + "database/sql" + + "github.com/gogf/gf/debug/gdebug" + "github.com/gogf/gf/frame/gins" + + "github.com/gogf/gf/database/gdb" + "github.com/gogf/gf/os/gtime" +) + +type User struct { + Id int `orm:"id,primary" json:"id"` + Passport string `orm:"passport" json:"passport"` + Password string `orm:"password" json:"password"` + NickName string `orm:"nickname" json:"nick_name"` + CreateTime *gtime.Time `orm:"create_time" json:"create_time"` +} + +type UserModel struct { + *gdb.Model + TableName string +} + +var ( + UserTableName = "user" + gUserModelCacheKey = gdebug.CallerFilePath() +) + +func ModelUser() *UserModel { + return gins.GetOrSetFunc(gUserModelCacheKey, func() interface{} { + return &UserModel{ + DB().Table(UserTableName).Safe(), + UserTableName, + } + }).(*UserModel) +} + +func (r *User) Insert() (result sql.Result, err error) { + return ModelUser().Data(r).Insert() +} + +func (r *User) Replace() (result sql.Result, err error) { + return ModelUser().Data(r).Replace() +} + +func (r *User) Save() (result sql.Result, err error) { + return ModelUser().Data(r).Save() +} + +func (r *User) Update() (result sql.Result, err error) { + return ModelUser().Data(r).Where(gdb.GetWhereConditionOfStruct(r)).Update() +} + +func (r *User) Delete() (result sql.Result, err error) { + return ModelUser().Where(gdb.GetWhereConditionOfStruct(r)).Delete() +} + +func (m *UserModel) Select() ([]*User, error) { + return m.All() +} + +func (m *UserModel) All() ([]*User, error) { + array := ([]*User)(nil) + if err := m.Scan(&array); err != nil { + return nil, err + } + return array, nil +} + +func (m *UserModel) One() (*User, error) { + list, err := m.All() + if err != nil { + return nil, err + } + if len(list) > 0 { + return list[0], nil + } + return nil, nil +} diff --git a/.example/net/ghttp/server/admin/admin.go b/.example/net/ghttp/server/admin/admin.go new file mode 100644 index 000000000..c7ad4e598 --- /dev/null +++ b/.example/net/ghttp/server/admin/admin.go @@ -0,0 +1,17 @@ +package main + +import ( + "github.com/gogf/gf/frame/g" + "github.com/gogf/gf/net/ghttp" +) + +func main() { + s := g.Server() + s.SetNameToUriType(ghttp.NAME_TO_URI_TYPE_FULLNAME) + s.EnableAdmin() + s.BindHandler("/", func(r *ghttp.Request) { + r.Response.Write("hello world") + }) + s.SetPort(8199) + s.Run() +} diff --git a/.example/os/gtimer/gtimer2.go b/.example/os/gtimer/gtimer2.go index 62f474c3d..8429a16b7 100644 --- a/.example/os/gtimer/gtimer2.go +++ b/.example/os/gtimer/gtimer2.go @@ -13,12 +13,12 @@ func main() { //w := gtimer.New(10, 10*time.Millisecond) fmt.Println("start:", time.Now()) for i := 0; i < 1000000; i++ { - gtimer.AddTimes(time.Second, 2, func() { + gtimer.AddTimes(time.Second, 1, func() { v.Add(1) }) } fmt.Println("end :", time.Now()) - time.Sleep(5000 * time.Millisecond) + time.Sleep(1000 * time.Millisecond) fmt.Println(v.Val(), time.Now()) //gtimer.AddSingleton(time.Second, func() { diff --git a/.example/other/test.go b/.example/other/test.go index cdccd9b43..4b25e1e98 100644 --- a/.example/other/test.go +++ b/.example/other/test.go @@ -1,10 +1,12 @@ package main import ( + "github.com/gogf/gf/frame/g" "github.com/gogf/gf/os/glog" ) func main() { - glog.Error("error") + v := g.NewVar(1) + glog.Error(v.String()) glog.Errorfln("error") } diff --git a/database/gdb/gdb_func.go b/database/gdb/gdb_func.go index 3a1ddfe6c..cffab7410 100644 --- a/database/gdb/gdb_func.go +++ b/database/gdb/gdb_func.go @@ -14,6 +14,8 @@ import ( "strings" "time" + "github.com/gogf/gf/internal/structs" + "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/text/gstr" "github.com/gogf/gf/util/gconv" @@ -24,6 +26,38 @@ type apiString interface { String() string } +const ( + OrmTagForStruct = "orm" + OrmTagForUnique = "unique" + OrmTagForPrimary = "primary" +) + +// 获得struct对象对应的where查询条件 +func GetWhereConditionOfStruct(pointer interface{}) (where string, args []interface{}) { + array := ([]string)(nil) + for tag, field := range structs.TagMapField(pointer, []string{OrmTagForStruct}, true) { + array = strings.Split(tag, ",") + if len(array) > 1 && gstr.InArray([]string{OrmTagForUnique, OrmTagForPrimary}, array[1]) { + return array[0], []interface{}{field.Value()} + } + if len(where) > 0 { + where += " " + } + where += tag + "=?" + args = append(args, field.Value()) + } + return +} + +// 获得orm标签与属性的映射关系 +func GetOrmMappingOfStruct(pointer interface{}) map[string]string { + mapping := make(map[string]string) + for tag, attr := range structs.TagMapName(pointer, []string{OrmTagForStruct}, true) { + mapping[strings.Split(tag, ",")[0]] = attr + } + return mapping +} + // 格式化SQL语句. func formatQuery(query string, args []interface{}) (newQuery string, newArgs []interface{}) { return handlerSliceArguments(query, args) @@ -138,7 +172,7 @@ func getInsertOperationByOption(option int) string { // 将对象转换为map,如果对象带有继承对象,那么执行递归转换。 // 该方法用于将变量传递给数据库执行之前。 func structToMap(obj interface{}) map[string]interface{} { - data := gconv.Map(obj) + data := gconv.Map(obj, OrmTagForStruct) for key, value := range data { rv := reflect.ValueOf(value) kind := rv.Kind() @@ -183,7 +217,7 @@ func bindArgsToQuery(query string, args []interface{}) string { } switch kind { case reflect.String, reflect.Map, reflect.Slice, reflect.Array: - return "'" + gconv.String(args[index]) + "'" + return "'" + gstr.QuoteMeta(gconv.String(args[index]), "'") + "'" } return gconv.String(args[index]) } @@ -194,5 +228,5 @@ func bindArgsToQuery(query string, args []interface{}) string { // 使用递归的方式将map键值对映射到struct对象上,注意参数是一个指向struct的指针。 func mapToStruct(data map[string]interface{}, pointer interface{}) error { - return gconv.StructDeep(data, pointer) + return gconv.StructDeep(data, pointer, GetOrmMappingOfStruct(pointer)) } diff --git a/database/gdb/gdb_model.go b/database/gdb/gdb_model.go index 22eae4b81..ef6a13d3e 100644 --- a/database/gdb/gdb_model.go +++ b/database/gdb/gdb_model.go @@ -490,21 +490,21 @@ func (md *Model) Value() (Value, error) { } // 链式操作,查询单条记录,并自动转换为struct对象, 参数必须为对象的指针,不能为空指针。 -func (md *Model) Struct(objPointer interface{}) error { +func (md *Model) Struct(pointer interface{}) error { one, err := md.One() if err != nil { return err } - return one.ToStruct(objPointer) + return one.ToStruct(pointer) } // 链式操作,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。 -func (md *Model) Structs(objPointerSlice interface{}) error { +func (md *Model) Structs(pointer interface{}) error { r, err := md.All() if err != nil { return err } - return r.ToStructs(objPointerSlice) + return r.ToStructs(pointer) } // 链式操作,将结果转换为指定的struct/*struct/[]struct/[]*struct, diff --git a/encoding/gjson/gjson_api_new_load.go b/encoding/gjson/gjson_api_new_load.go index 12a95cc46..53384901c 100644 --- a/encoding/gjson/gjson_api_new_load.go +++ b/encoding/gjson/gjson_api_new_load.go @@ -12,6 +12,8 @@ import ( "encoding/json" "errors" "fmt" + "reflect" + "github.com/gogf/gf/encoding/gini" "github.com/gogf/gf/encoding/gtoml" "github.com/gogf/gf/encoding/gxml" @@ -21,7 +23,6 @@ import ( "github.com/gogf/gf/os/gfile" "github.com/gogf/gf/text/gregex" "github.com/gogf/gf/util/gconv" - "reflect" ) // New creates a Json object with any variable type of , @@ -186,16 +187,14 @@ func doLoadContent(dataType string, data []byte, safe ...bool) (*Json, error) { if err != nil { return nil, err } - if result == nil { - decoder := json.NewDecoder(bytes.NewReader(data)) - decoder.UseNumber() - if err := decoder.Decode(&result); err != nil { - return nil, err - } - switch result.(type) { - case string, []byte: - return nil, fmt.Errorf(`json decoding failed for content: %s`, string(data)) - } + decoder := json.NewDecoder(bytes.NewReader(data)) + decoder.UseNumber() + if err := decoder.Decode(&result); err != nil { + return nil, err + } + switch result.(type) { + case string, []byte: + return nil, fmt.Errorf(`json decoding failed for content: %s`, string(data)) } return New(result, safe...), nil } diff --git a/net/ghttp/ghttp_server_service_controller.go b/net/ghttp/ghttp_server_service_controller.go index 50f9830fe..aca597b75 100644 --- a/net/ghttp/ghttp_server_service_controller.go +++ b/net/ghttp/ghttp_server_service_controller.go @@ -83,7 +83,7 @@ func (s *Server) BindController(pattern string, c Controller, methods ...string) // 这里处理新增/user路由绑定。 // 注意,当pattern带有内置变量时,不会自动加该路由。 if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) { - p := gstr.PosR(key, "/index") + p := gstr.PosRI(key, "/index") k := key[0:p] + key[p+6:] if len(k) == 0 || k[0] == '@' { k = "/" + k diff --git a/net/ghttp/ghttp_server_service_object.go b/net/ghttp/ghttp_server_service_object.go index ddc39a90a..3ed5568cc 100644 --- a/net/ghttp/ghttp_server_service_object.go +++ b/net/ghttp/ghttp_server_service_object.go @@ -87,7 +87,7 @@ func (s *Server) BindObject(pattern string, obj interface{}, methods ...string) // 如果方法中带有Index方法,那么额外自动增加一个路由规则匹配主URI。 // 注意,当pattern带有内置变量时,不会自动加该路由。 if strings.EqualFold(mname, "Index") && !gregex.IsMatchString(`\{\.\w+\}`, pattern) { - p := gstr.PosR(key, "/index") + p := gstr.PosRI(key, "/index") k := key[0:p] + key[p+6:] if len(k) == 0 || k[0] == '@' { k = "/" + k diff --git a/os/gfile/gfile_source.go b/os/gfile/gfile_source.go index 83151e139..5b12dc10c 100644 --- a/os/gfile/gfile_source.go +++ b/os/gfile/gfile_source.go @@ -45,6 +45,7 @@ func MainPkgPath() string { if path != "" { return path } + lastFile := "" for i := 1; i < 10000; i++ { if _, file, _, ok := runtime.Caller(i); ok { if goRootForFilter != "" && len(file) >= len(goRootForFilter) && file[0:len(goRootForFilter)] == goRootForFilter { @@ -58,21 +59,26 @@ func MainPkgPath() string { if Ext(file) != ".go" { continue } - // separator of '/' will be converted to Separator. - for path = Dir(file); len(path) > 1 && Exists(path) && path[len(path)-1] != os.PathSeparator; { - files, _ := ScanDir(path, "*.go") - for _, v := range files { - if gregex.IsMatchString(`package\s+main`, GetContents(v)) { - mainPkgPath.Set(path) - return path - } - } - path = Dir(path) + lastFile = file + if gregex.IsMatchString(`package\s+main`, GetContents(file)) { + mainPkgPath.Set(Dir(file)) + return Dir(file) } - } else { break } } + if lastFile != "" { + for path = Dir(lastFile); len(path) > 1 && Exists(path) && path[len(path)-1] != os.PathSeparator; { + files, _ := ScanDir(path, "*.go") + for _, v := range files { + if gregex.IsMatchString(`package\s+main`, GetContents(v)) { + mainPkgPath.Set(path) + return path + } + } + path = Dir(path) + } + } return "" } diff --git a/os/gres/gres.go b/os/gres/gres.go index 159eb8131..f9be294ea 100644 --- a/os/gres/gres.go +++ b/os/gres/gres.go @@ -7,19 +7,6 @@ // Package gres provides resource management and packing/unpacking feature between files and bytes. package gres -const ( - gPACKAGE_TEMPLATE = `package %s - -import "github.com/gogf/gf/os/gres" - -func init() { - if err := gres.Add(%s); err != nil { - panic(err) - } -} -` -) - var ( // Default resource object. defaultResource = Instance() diff --git a/os/gres/gres_func.go b/os/gres/gres_func.go index 72ad6ff92..112ef5252 100644 --- a/os/gres/gres_func.go +++ b/os/gres/gres_func.go @@ -16,6 +16,19 @@ import ( "github.com/gogf/gf/os/gfile" ) +const ( + gPACKAGE_TEMPLATE = `package %s + +import "github.com/gogf/gf/os/gres" + +func init() { + if err := gres.Add(%s); err != nil { + panic(err) + } +} +` +) + // Pack packs the path specified by into bytes. // The unnecessary parameter indicates the prefix for each file // packed into the result bytes. diff --git a/text/gstr/gstr.go b/text/gstr/gstr.go index ca58da524..1f74d2564 100644 --- a/text/gstr/gstr.go +++ b/text/gstr/gstr.go @@ -569,14 +569,22 @@ func StripSlashes(str string) string { } // QuoteMeta returns a version of str with a backslash character (\) -// before every character that is among: -// .\+*?[^]($) -func QuoteMeta(str string) string { +// before every character that is among: .\+*?[^]($) +func QuoteMeta(str string, chars ...string) string { var buf bytes.Buffer for _, char := range str { - switch char { - case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?': - buf.WriteRune('\\') + if len(chars) > 0 { + for _, c := range chars[0] { + if c == char { + buf.WriteRune('\\') + break + } + } + } else { + switch char { + case '.', '+', '\\', '(', '$', ')', '[', '^', ']', '*', '?': + buf.WriteRune('\\') + } } buf.WriteRune(char) } diff --git a/text/gstr/gstr_z_unit_basic_test.go b/text/gstr/gstr_z_unit_basic_test.go index 8f163949f..d2be8258f 100644 --- a/text/gstr/gstr_z_unit_basic_test.go +++ b/text/gstr/gstr_z_unit_basic_test.go @@ -315,6 +315,9 @@ func Test_StripSlashes(t *testing.T) { func Test_QuoteMeta(t *testing.T) { gtest.Case(t, func() { gtest.Assert(gstr.QuoteMeta(`.\+*?[^]($)`), `\.\\\+\*\?\[\^\]\(\$\)`) + gtest.Assert(gstr.QuoteMeta(`.\+*中国?[^]($)`), `\.\\\+\*中国\?\[\^\]\(\$\)`) + gtest.Assert(gstr.QuoteMeta(`.''`, `'`), `.\'\'`) + gtest.Assert(gstr.QuoteMeta(`中国.''`, `'`), `中国.\'\'`) }) }