improve model feature

This commit is contained in:
John
2019-08-30 20:29:12 +08:00
parent a31108e753
commit ee89a06b3e
17 changed files with 232 additions and 53 deletions

View File

@ -0,0 +1,4 @@
# MySQL数据库配置
[database]
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"

View File

@ -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())
}

View File

@ -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()
}

View File

@ -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
}

View File

@ -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()
}

View File

@ -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() {

View File

@ -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")
}

View File

@ -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对象上注意参数<pointer>是一个指向struct的指针。
func mapToStruct(data map[string]interface{}, pointer interface{}) error {
return gconv.StructDeep(data, pointer)
return gconv.StructDeep(data, pointer, GetOrmMappingOfStruct(pointer))
}

View File

@ -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,

View File

@ -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 <data>,
@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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 <file> '/' 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 ""
}

View File

@ -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()

View File

@ -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 <srcPath> into bytes.
// The unnecessary parameter <keyPrefix> indicates the prefix for each file
// packed into the result bytes.

View File

@ -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)
}

View File

@ -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(`中国.''`, `'`), `中国.\'\'`)
})
}